dubbo源码解析(十四)远程通信——Http
远程通讯——Http
目标:介绍基于Http的来实现的远程通信、介绍dubbo-remoting-http内的源码解析。
前言
本文我们讲解的是如何基于Tomcat或者Jetty实现HTTP服务器。Tomcat和Jetty都是一种servlet引擎,Jetty要比Tomcat的架构更简单一些。关于它们之间的比较,我觉得google一些更加方便,我就不多废话了 。
下面是dubbo-remoting-http包下的类图:
可以看到这个包下只提供了服务端实现,并没有客户端实现。
源码分析
(一)HttpServer
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服务器的接口,定义了服务器相关的方法,都比较好理解。
(二)AbstractHttpServer
该类实现接口HttpServer,是http服务器接口的抽象类。
/** * 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; }
该类中有三个属性,关键是看在后面怎么用到,因为该类是抽象类,所以该类中的方法并没有实现具体的逻辑,我们继续往下看它的三个子类。
(三)TomcatHttpServer
该类是基于Tomcat来实现服务器的实现类,它继承了AbstractHttpServer。
1.属性
/** * 内嵌的tomcat对象 */ private final Tomcat tomcat; /** * url对象 */ private final URL url;
该类的两个属性,关键是内嵌的tomcat。
2.构造方法
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配置经验的朋友应该看起来很简单。
3.close
@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.stop
(四)JettyHttpServer
该类是基于Jetty来实现服务器的实现类,它继承了AbstractHttpServer。
1.属性
/** * 内嵌的Jetty服务器对象 */ private Server server; /** * url对象 */ private URL url;
该类的两个属性,关键是内嵌的sever,它是内嵌的etty服务器对象。
2.构造方法
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服务器。
3.close
@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方法。
(五)ServletHttpServer
该类继承了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上。
(六)HttpBinder
@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。它有三个实现类,请往下看。
(七)TomcatHttpBinder
public class TomcatHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { // 创建一个TomcatHttpServer return new TomcatHttpServer(url, handler); } }
一眼就看出来,就是创建了个基于Tomcat实现的服务器TomcatHttpServer对象。具体的就往上看TomcatHttpServer里面的实现。
(八)JettyHttpBinder
public class JettyHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { // 创建JettyHttpServer实例 return new JettyHttpServer(url, handler); } }
一眼就看出来,就是创建了个基于Jetty实现的服务器JettyHttpServer对象。具体的就往上看JettyHttpServer里面的实现。
(九)ServletHttpBinder
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机制。
(十)BootstrapListener
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 中。
(十一)DispatcherServlet
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
(十二)ServletManager
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。
(十三)HttpHandler
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实现远程通信部分。