1.概述
Dubbo 的 HTTP 服務器,在 dubbo-remoting-http 模塊中實現,使用在 http://、 rest://、hessian://、webservice://協議上。
dubbo-remoting-http 模塊,類圖如下:
2.API
2.1 HttpServer
實現 Resetable 接口,HTTP 服務器接口。方法如下:
/**
* HTTP 服務器接口
*/
public interface HttpServer extends Resetable {
/**
* get http handler.
*
* @return http handler.
*/
HttpHandler getHttpHandler();
/**
* get url.
*
* @return url
*/
URL getUrl();
/**
* get local address.
*
* @return local address.
*/
InetSocketAddress getLocalAddress();
/**
* close the channel.
*/
void close();
/**
* Graceful close the channel.
*/
void close(int timeout);
/**
* is bound.
*
* @return bound
*/
boolean isBound();
/**
* is closed.
*
* @return closed
*/
boolean isClosed();
}
2.2 AbstractHttpServer
實現 HttpServer 接口,HTTP 服務器抽象類。代碼如下:
/**
* AbstractHttpServer
*
* HTTP 服務器抽象類
*/
public abstract class AbstractHttpServer implements HttpServer {
/**
* URL 對象
*/
private final URL url;
/**
* 處理器
*/
private final HttpHandler handler;
/**
* 是否關閉
*/
private volatile boolean closed;
public AbstractHttpServer(URL url, HttpHandler handler) {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
this.url = url;
this.handler = handler;
}
@Override
public HttpHandler getHttpHandler() {
return handler;
}
@Override
public URL getUrl() {
return url;
}
@Override
public void reset(URL url) {
}
@Override
public boolean isBound() {
return true;
}
@Override
public InetSocketAddress getLocalAddress() {
return url.toInetSocketAddress();
}
@Override
public void close() {
closed = true;
}
@Override
public void close(int timeout) {
close();
}
@Override
public boolean isClosed() {
return closed;
}
}
2.3 HttpHandler
HTTP 處理器接口。方法如下:
/**
* http invocation handler.
*
* HTTP 處理器接口
*/
public interface HttpHandler {
/**
* invoke.
*
* 處理器請求
*
* @param request request. 請求
* @param response response. 響應
* @throws IOException 當 IO 發生異常
* @throws ServletException 當 Servlet 發生異常
*/
void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
}
2.4 HttpBinder
HTTP 綁定器接口。方法如下:
/**
* HttpBinder
*
* HTTP 綁定起接口
*/
@SPI("jetty")
public interface HttpBinder {
/**
* bind the server.
*
* @param url server url.
* @return server.
*/
@Adaptive({Constants.SERVER_KEY})
HttpServer bind(URL url, HttpHandler handler);
}
2.5 DispatcherServlet
實現 javax.servlet.http.HttpServlet 接口,服務請求調度 Servlet。代碼如下:
/**
* Service dispatcher Servlet.
*
* 服務調度 Servlet
*/
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 5766349180380479888L;
/**
* 處理器集合
*
* key:服務器端口
*/
private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>();
/**
* 單例
*/
private static DispatcherServlet INSTANCE;
public DispatcherServlet() {
DispatcherServlet.INSTANCE = this;
}
public static DispatcherServlet getInstance() {
return INSTANCE;
}
/**
* 添加處理器
*
* @param port 服務器端口
* @param processor 處理器
*/
public static void addHttpHandler(int port, HttpHandler processor) {
handlers.put(port, processor);
}
/**
* 移除處理器
*
* @param port 服務器端口
*/
public static void removeHttpHandler(int port) {
handlers.remove(port);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 獲得處理器
HttpHandler handler = handlers.get(request.getLocalPort());
// 處理器不存在,報錯
if (handler == null) {// service not found.
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
// 處理請求
} else {
handler.handle(request, response);
}
}
}
2.6 ServletManager
Servlet 管理器,負責管理 ServletContext ,目前僅有 dubbo-rpc-rest 模塊,需要使用到這個類。代碼如下:
public class ServletManager {
/**
* 外部服務器端口,用於 `servlet` 的服務器端口
*/
public static final int EXTERNAL_SERVER_PORT = -1234;
/**
* 單例
*/
private static final ServletManager instance = new ServletManager();
/**
* ServletContext 集合
*/
private final Map<Integer, ServletContext> contextMap = new ConcurrentHashMap<Integer, ServletContext>();
public static ServletManager getInstance() {
return instance;
}
/**
* 添加 ServletContext 對象
*
* @param port 服務器端口
* @param servletContext ServletContext 對象
*/
public void addServletContext(int port, ServletContext servletContext) {
contextMap.put(port, servletContext);
}
/**
* 移除 ServletContext 對象
*
* @param port 服務器端口
*/
public void removeServletContext(int port) {
contextMap.remove(port);
}
/**
* 獲得 ServletContext 對象
*
* @param port 服務器端口
* @return ServletContext 對象
*/
public ServletContext getServletContext(int port) {
return contextMap.get(port);
}
}
3. Tomcat 實現
3.1 TomcatHttpServer
實現 AbstractHttpServer 抽象類,基於 Tomcat 的 HTTP 服務器實現類。
/**
* 基於 Tomcat 的 HTTP 服務器實現類
*/
public class TomcatHttpServer extends AbstractHttpServer {
private static final Logger logger = LoggerFactory.getLogger(TomcatHttpServer.class);
/**
* 內嵌的 Tomcat 對象
*/
private final Tomcat tomcat;
/**
* URL 對象
*/
private final URL url;
public TomcatHttpServer(URL url, final HttpHandler handler) {
super(url, handler);
this.url = url;
// 註冊 HttpHandler 到 DispatcherServlet 中
DispatcherServlet.addHttpHandler(url.getPort(), handler);
// 創建內嵌的 Tomcat 對象
String baseDir = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
tomcat = new Tomcat();
tomcat.setBaseDir(baseDir);
tomcat.setPort(url.getPort());
tomcat.getConnector().setProperty("maxThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS))); // 最大線程數
// tomcat.getConnector().setProperty(
// "minSpareThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS)));
tomcat.getConnector().setProperty("maxConnections", String.valueOf(url.getParameter(Constants.ACCEPTS_KEY, -1))); // 最大連接池
tomcat.getConnector().setProperty("URIEncoding", "UTF-8"); // 編碼爲 UTF-8
tomcat.getConnector().setProperty("connectionTimeout", "60000"); // 連接超時,60 秒
tomcat.getConnector().setProperty("maxKeepAliveRequests", "-1");
tomcat.getConnector().setProtocol("org.apache.coyote.http11.Http11NioProtocol");
// 添加 DispatcherServlet 到 Tomcat 中
Context context = tomcat.addContext("/", baseDir);
Tomcat.addServlet(context, "dispatcher", new DispatcherServlet());
context.addServletMapping("/*", "dispatcher");
// 添加 ServletContext 對象,到 ServletManager 中
ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext());
// 啓動 Tomcat
try {
tomcat.start();
} catch (LifecycleException e) {
throw new IllegalStateException("Failed to start tomcat server at " + url.getAddress(), e);
}
}
}