目標:介紹基於Http的來實現的遠程通訊、介紹dubbo-remoting-http內的源碼解析。
本文咱們講解的是如何基於Tomcat或者Jetty實現HTTP服務器。Tomcat和Jetty都是一種servlet引擎,Jetty要比Tomcat的架構更簡單一些。關於它們之間的比較,我以爲google一些更加方便,我就很少廢話了 。java
下面是dubbo-remoting-http包下的類圖:git
能夠看到這個包下只提供了服務端實現,並無客戶端實現。github
public interface HttpServer extends Resetable { /** * get http handler. * 得到http的處理類 * @return http handler. */ HttpHandler getHttpHandler(); /** * get url. * 得到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(); }
該接口是http服務器的接口,定義了服務器相關的方法,都比較好理解。web
該類實現接口HttpServer,是http服務器接口的抽象類。apache
/** * url */ private final URL url; /** * http服務器處理器 */ 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; }
該類中有三個屬性,關鍵是看在後面怎麼用到,由於該類是抽象類,因此該類中的方法並無實現具體的邏輯,咱們繼續往下看它的三個子類。tomcat
該類是基於Tomcat來實現服務器的實現類,它繼承了AbstractHttpServer。服務器
/** * 內嵌的tomcat對象 */ private final Tomcat tomcat; /** * url對象 */ private final URL url;
該類的兩個屬性,關鍵是內嵌的tomcat。架構
public TomcatHttpServer(URL url, final HttpHandler handler) { super(url, handler); this.url = url; // 添加處理器 DispatcherServlet.addHttpHandler(url.getPort(), handler); // 得到java.io.tmpdir的絕對路徑目錄 String baseDir = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath(); // 建立內嵌的tomcat對象 tomcat = new Tomcat(); // 設置根目錄 tomcat.setBaseDir(baseDir); // 設置端口號 tomcat.setPort(url.getPort()); // 給默認的http鏈接器。設置最大線程數 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))); // 設置URL編碼格式 tomcat.getConnector().setProperty("URIEncoding", "UTF-8"); // 設置鏈接超時事件爲60s tomcat.getConnector().setProperty("connectionTimeout", "60000"); // 設置最大長鏈接個數爲不限制個數 tomcat.getConnector().setProperty("maxKeepAliveRequests", "-1"); // 設置將由鏈接器使用的Coyote協議。 tomcat.getConnector().setProtocol("org.apache.coyote.http11.Http11NioProtocol"); // 添加上下文 Context context = tomcat.addContext("/", baseDir); // 添加servlet,把servlet添加到context Tomcat.addServlet(context, "dispatcher", new DispatcherServlet()); // 添加servlet映射 context.addServletMapping("/*", "dispatcher"); // 添加servlet上下文 ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext()); try { // 開啓tomcat tomcat.start(); } catch (LifecycleException e) { throw new IllegalStateException("Failed to start tomcat server at " + url.getAddress(), e); } }
該方法的構造函數中就啓動了tomcat,前面不少都是對tomcat的啓動參數以及配置設置。若是有過tomcat配置經驗的朋友應該看起來很簡單。app
@Override public void close() { super.close(); // 移除相關的servlet上下文 ServletManager.getInstance().removeServletContext(url.getPort()); try { // 中止tomcat tomcat.stop(); } catch (Exception e) { logger.warn(e.getMessage(), e); } }
該方法是關閉服務器的方法。調用了tomcat.stopide
該類是基於Jetty來實現服務器的實現類,它繼承了AbstractHttpServer。
/** * 內嵌的Jetty服務器對象 */ private Server server; /** * url對象 */ private URL url;
該類的兩個屬性,關鍵是內嵌的sever,它是內嵌的etty服務器對象。
public JettyHttpServer(URL url, final HttpHandler handler) { super(url, handler); this.url = url; // TODO we should leave this setting to slf4j // we must disable the debug logging for production use // 設置日誌 Log.setLog(new StdErrLog()); // 禁用調試用的日誌 Log.getLog().setDebugEnabled(false); // 添加http服務器處理器 DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), handler); // 得到線程數 int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS); // 建立線程池 QueuedThreadPool threadPool = new QueuedThreadPool(); // 設置線程池配置 threadPool.setDaemon(true); threadPool.setMaxThreads(threads); threadPool.setMinThreads(threads); // 建立選擇NIO鏈接器 SelectChannelConnector connector = new SelectChannelConnector(); // 得到綁定的ip String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost()); if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) { // 設置主機地址 connector.setHost(bindIp); } // 設置端口號 connector.setPort(url.getParameter(Constants.BIND_PORT_KEY, url.getPort())); // 建立Jetty服務器對象 server = new Server(); // 設置線程池 server.setThreadPool(threadPool); // 設置鏈接器 server.addConnector(connector); // 添加DispatcherServlet到jetty ServletHandler servletHandler = new ServletHandler(); ServletHolder servletHolder = servletHandler.addServletWithMapping(DispatcherServlet.class, "/*"); servletHolder.setInitOrder(2); // dubbo's original impl can't support the use of ServletContext // server.addHandler(servletHandler); // TODO Context.SESSIONS is the best option here? Context context = new Context(server, "/", Context.SESSIONS); context.setServletHandler(servletHandler); // 添加 ServletContext 對象,到 ServletManager 中 ServletManager.getInstance().addServletContext(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), context.getServletContext()); try { // 啓動jetty服務器 server.start(); } catch (Exception e) { throw new IllegalStateException("Failed to start jetty server on " + url.getParameter(Constants.BIND_IP_KEY) + ":" + url.getParameter(Constants.BIND_PORT_KEY) + ", cause: " + e.getMessage(), e); } }
能夠看到它跟TomcatHttpServer中構造函數不一樣的是API的不一樣,不過思路差很少,先設置啓動參數和配置,而後啓動jetty服務器。
@Override public void close() { super.close(); // 移除 ServletContext 對象 ServletManager.getInstance().removeServletContext(url.getParameter(Constants.BIND_PORT_KEY, url.getPort())); if (server != null) { try { // 中止服務器 server.stop(); } catch (Exception e) { logger.warn(e.getMessage(), e); } } }
該方法是關閉服務器的方法,調用的是server的stop方法。
該類繼承了AbstractHttpServer,是基於 Servlet 的服務器實現類。
public class ServletHttpServer extends AbstractHttpServer { public ServletHttpServer(URL url, HttpHandler handler) { super(url, handler); // /把 HttpHandler 到 DispatcherServlet 中,默認端口爲8080 DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, 8080), handler); } }
該類就一個構造方法。就是把服務器處理器註冊到DispatcherServlet上。
@SPI("jetty") public interface HttpBinder { /** * bind the server. * 綁定到服務器 * @param url server url. * @return server. */ @Adaptive({Constants.SERVER_KEY}) HttpServer bind(URL url, HttpHandler handler); }
該接口是http綁定器接口,其中就定義了一個方法就是綁定方法,而且返回服務器對象。該接口是一個可擴展接口,默認實現JettyHttpBinder。它有三個實現類,請往下看。
public class TomcatHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { // 建立一個TomcatHttpServer return new TomcatHttpServer(url, handler); } }
一眼就看出來,就是建立了個基於Tomcat實現的服務器TomcatHttpServer對象。具體的就往上看TomcatHttpServer裏面的實現。
public class JettyHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { // 建立JettyHttpServer實例 return new JettyHttpServer(url, handler); } }
一眼就看出來,就是建立了個基於Jetty實現的服務器JettyHttpServer對象。具體的就往上看JettyHttpServer裏面的實現。
public class ServletHttpBinder implements HttpBinder { @Override @Adaptive() public HttpServer bind(URL url, HttpHandler handler) { // 建立ServletHttpServer對象 return new ServletHttpServer(url, handler); } }
建立了個基於servlet實現的服務器ServletHttpServer對象。而且方法上加入了Adaptive,用到了dubbo SPI機制。
public class BootstrapListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { // context建立的時候,把ServletContext添加到ServletManager ServletManager.getInstance().addServletContext(ServletManager.EXTERNAL_SERVER_PORT, servletContextEvent.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { // context銷燬到時候,把servletContextEvent移除 ServletManager.getInstance().removeServletContext(ServletManager.EXTERNAL_SERVER_PORT); } }
該類實現了ServletContextListener,是 啓動監聽器,當context建立和銷燬的時候對ServletContext作處理。不過須要配置BootstrapListener到web.xml,經過這樣的方式,讓外部的 ServletContext 對象,添加到 ServletManager 中。
public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 5766349180380479888L; /** * http服務器處理器 */ private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>(); /** * 單例 */ private static DispatcherServlet INSTANCE; public DispatcherServlet() { DispatcherServlet.INSTANCE = this; } /** * 添加處理器 * @param port * @param processor */ public static void addHttpHandler(int port, HttpHandler processor) { handlers.put(port, processor); } public static void removeHttpHandler(int port) { handlers.remove(port); } public static DispatcherServlet getInstance() { return INSTANCE; } @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 得到處理器 HttpHandler handler = handlers.get(request.getLocalPort()); // 若是處理器不存在 if (handler == null) {// service not found. // 返回404 response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found."); } else { // 處理請求 handler.handle(request, response); } } }
該類繼承了HttpServlet,是服務請求調度servlet類,主要是service方法,根據請求來調度不一樣的處理器去處理請求,若是沒有該處理器,則報錯404
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 */ 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 */ public ServletContext getServletContext(int port) { return contextMap.get(port); } }
該類是servlet的管理器,管理着ServletContext。
public interface HttpHandler { /** * invoke. * HTTP請求處理 * @param request request. * @param response response. * @throws IOException * @throws ServletException */ void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; }
該接口是HTTP 處理器接口,就定義了一個處理請求的方法。
該部分相關的源碼解析地址: https://github.com/CrazyHZM/i...
該文章講解了內嵌tomcat和jetty的來實現的http服務器,關鍵須要對tomcat和jetty的配置有所瞭解。下一篇我會講解基於mina實現遠程通訊部分。