HTTP服务器(八)
本文基于 Dubbo 2.6.1 版本,望知悉。
1. 概述
本文,我们来分享 Dubbo 的 HTTP 服务器,在 dubbo-remoting-http 模块中实现,使用在 http://、 rest://、hessian://、webservice://协议上。
dubbo-remoting-http
只提供 Server 部分
,不同于前面分享的 Dubbo 的 NIO 服务器(
dubbo-remoting-api
),提供 Client 和 Server 。代码结构如下:
代码结构
- API 层:
- 最外层:API 定义。
- support 包: 公用实现。
- 实现层:
- jetty内嵌的 包:基于 Jetty 实现,版本为 6.x 。
- tomcat内嵌的 包:基于 Tomcat 实现,版本为 8.x 。
- servlet外部的 包:基于 Servlet Bridge Server 实现。简单的说,使用 war 包,部署在 Tomcat 、Jetty 等 Servlet 容器。
在 《Dubbo 用户指南 —— http://》 文档中,分享了具体的配置方式。这块的文档,写的比较简略,如果看不太明白的胖友,可以看看 《Dubbo 组成原理 - http服务消费端如何调用》 作为补充。
另外,文档中推荐使用 Servlet Bridge Server 的部署方式,可能是文档写的比较早,现在主流是的 Fat Jar 的方式,所以实际使用时,jetty 或 tomcat 方式更为适合。
当然,能够方便的通过配置的方式,切换具体的 HTTP 服务的拓展,依托于 Dubbo SPI 的机制。
2. 原理
dubbo-remoting-http 模块,类图如下:
代码结构
- HttpBinder ,负责创建对应的 HttpServer 对象。
- 不同的 Protocol ,实现各自的 HttpHandler 类。并且,暴露服务时,启动 HttpServer 的同时,创建对应的 HttpHandler 对象,以 port 为键,注册到 DispatcherServlet 上。
- DispatcherServlet ,核心 ,调度请求,到对应的 HttpHandler 中。
整体流程
如下:
流程
3. API
3.1 HttpServer
com.alibaba.dubbo.remoting.http.HttpServer ,实现 Resetable 接口,HTTP 服务器接口。方法如下:
```plain text plain // 处理器 HttpHandler getHttpHandler(); // 【属性相关】 URL getUrl(); InetSocketAddress getLocalAddress(); // 【状态相关】 boolean isBound(); void close(); void close(int timeout); boolean isClosed();
1
2
3
4
5
6
7
8
9
---
### 3.1.1 AbstractHttpServer
[com.alibaba.dubbo.remoting.http.AbstractHttpServer](https://github.com/YunaiV/dubbo/blob/master/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/AbstractHttpServer.java) ,实现 HttpServer 接口,HTTP 服务器抽象类。代码如下:
```plain text
plain 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; } }
3.2 HttpHandler
com.alibaba.dubbo.remoting.http.HttpHandler ,HTTP 处理器接口。方法如下:
```plain text plain /** * invoke. * * 处理器请求 * * @param request request. 请求 * @param response response. 响应 * @throws IOException 当 IO 发生异常 * @throws ServletException 当 Servlet 发生异常 */ void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
1
2
3
4
5
6
7
8
9
---
## 3.3 HttpBinder
[com.alibaba.dubbo.remoting.http.HttpBinder](https://github.com/YunaiV/dubbo/blob/master/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java) ,HTTP **绑定器**接口。方法如下:
```plain text
plain @SPI("jetty") public interface HttpBinder { /** * bind the server. * * @param url server url. * @return server. */ @Adaptive({Constants.SERVER_KEY}) HttpServer bind(URL url, HttpHandler handler); }
- @SPI(“jetty”)拓展点 注解,Dubbo SPI ,默认为 “jetty” ,即未配置情况下,使用 Jetty Server 。
- @Adaptive({Constants.SERVER_KEY}) 注解,基于 Dubbo SPI Adaptive 机制,加载对应的 Server 实现,使用 URL.server 属性。
3.4 DispatcherServlet
com.alibaba.dubbo.remoting.http.serlvet.DispatcherServlet ,实现 javax.servlet.http.HttpServlet 接口,服务请求调度 Servlet。代码如下:
```plain text plain 1: public class DispatcherServlet extends HttpServlet { 2: 3: /** 4: * 处理器集合 5: * 6: * key:服务器端口 7: */ 8: private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>(); 9: /** 10: * 单例 11: */ 12: private static DispatcherServlet INSTANCE; 13: 14: public DispatcherServlet() { 15: DispatcherServlet.INSTANCE = this; 16: } 17: 18: public static DispatcherServlet getInstance() { 19: return INSTANCE; 20: } 21: 22: /** 23: * 添加处理器 24: * 25: * @param port 服务器端口 26: * @param processor 处理器 27: */ 28: public static void addHttpHandler(int port, HttpHandler processor) { 29: handlers.put(port, processor); 30: } 31: 32: /** 33: * 移除处理器 34: * 35: * @param port 服务器端口 36: */ 37: public static void removeHttpHandler(int port) { 38: handlers.remove(port); 39: } 40: 41: @Override 42: protected void service(HttpServletRequest request, HttpServletResponse response) 43: throws ServletException, IOException { 44: // 获得处理器 45: HttpHandler handler = handlers.get(request.getLocalPort()); 46: // 处理器不存在,报错 47: if (handler == null) {// service not found. 48: response.sendError(HttpServletResponse.SC_NOT_FOUND, “Service not found.”); 49: // 处理请求 50: } else { 51: handler.handle(request, response); 52: } 53: } 54: 55: }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
- handlers**静态**
属性,处理器集合。
- #addHttpHandler(port, HttpHandler)
方法,注册处理器。
- #removeHttpHandler(port)
方法,取消处理器。
- #service(request, response)**实现**
方法,调度请求。
- 第 45 行:基于端口,获得处理器。
- 第 46 至 48 行:处理器不存在,返回 500 报错。
- 第 49 至 52 行:调用
HttpHandler#handle(request, response)
方法,处理请求,从而调度到 Service 的对应的方法。
## 3.5 ServletManager
[com.alibaba.dubbo.remoting.http.serlvet.ServletManager](https://github.com/YunaiV/dubbo/blob/master/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletManager.java) ,Servlet 管理器,负责管理 ServletContext ,目前仅有 dubbo-rpc-rest 模块,需要使用到这个类。代码如下:
```plain text
plain 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); } }
- EXTERNAL_SERVER_PORT静态外部 属性, 服务器端口,用于 servlet 的服务器端口。
- contextMap静态 属性,ServletContext 集合。
- #addServletContext(port, ServletContext) 方法,添加。
- #removeServletContext(port) 方法,移除。
- #getServletContext(port) 方法,查询。
4. Tomcat 实现
4.1 TomcatHttpServer
com.alibaba.dubbo.remoting.http.tomcat.TomcatHttpServer ,实现 AbstractHttpServer 抽象类,基于 Tomcat 的 HTTP 服务器实现类。
4.1.1 构造方法
```plain text plain 1: /** 2: * 内嵌的 Tomcat 对象 3: / 4: private final Tomcat tomcat; 5: /** 6: * URL 对象 7: */ 8: private final URL url; 9: 10: public TomcatHttpServer(URL url, final HttpHandler handler) { 11: super(url, handler); 12: this.url = url; 13: 14: // 注册 HttpHandler 到 DispatcherServlet 中 15: DispatcherServlet.addHttpHandler(url.getPort(), handler); 16: 17: // 创建内嵌的 Tomcat 对象 18: String baseDir = new File(System.getProperty(“java.io.tmpdir”)).getAbsolutePath(); 19: tomcat = new Tomcat(); 20: tomcat.setBaseDir(baseDir); 21: tomcat.setPort(url.getPort()); 22: tomcat.getConnector().setProperty(“maxThreads”, String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS))); // 最大线程数 23: // tomcat.getConnector().setProperty( 24: // “minSpareThreads”, String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS))); 25: tomcat.getConnector().setProperty(“maxConnections”, String.valueOf(url.getParameter(Constants.ACCEPTS_KEY, -1))); // 最大连接池 26: tomcat.getConnector().setProperty(“URIEncoding”, “UTF-8”); // 编码为 UTF-8 27: tomcat.getConnector().setProperty(“connectionTimeout”, “60000”); // 连接超时,60 秒 28: tomcat.getConnector().setProperty(“maxKeepAliveRequests”, “-1”); 29: tomcat.getConnector().setProtocol(“org.apache.coyote.http11.Http11NioProtocol”); 30: 31: // 添加 DispatcherServlet 到 Tomcat 中 32: Context context = tomcat.addContext(“/”, baseDir); 33: Tomcat.addServlet(context, “dispatcher”, new DispatcherServlet()); 34: context.addServletMapping(“/”, “dispatcher”); 35: 36: // 添加 ServletContext 对象,到 ServletManager 中 37: ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext()); 38: 39: // 启动 Tomcat 40: try { 41: tomcat.start(); 42: } catch (LifecycleException e) { 43: throw new IllegalStateException(“Failed to start tomcat server at “ + url.getAddress(), e); 44: } 45: }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
- 第 15 行:调用
DispatcherServlet#addHttpHandler(port, handler)
方法,注册 HttpHandler 到 DispatcherServlet 中。
- 第 17 至 29 行:**创建**
内嵌的 Tomcat 对象。
- 第 31 至 34 行:**创建**
并添加 DispatcherServlet 对象,到 Tomcat 中。
- 第 37 行:调用
ServletManager#addServletContext(port, ServletContext)
方法,添加 DispatcherServlet 对象,到 ServletManager 中。
- 第 39 至 44 行:调用 **启动**
Tomcat#start()
方法,
Tomcat 。
### 4.1.2 关闭
```plain text
plain @Override public void close() { // 标记关闭 super.close(); // 移除 ServletContext 对象 ServletManager.getInstance().removeServletContext(url.getPort()); // 关闭 Tomcat try { tomcat.stop(); } catch (Exception e) { logger.warn(e.getMessage(), e); } }
- 缺少 ,调用 DispacherServlet#remove(port) 方法,将 HttpHandler 对象,移除出 DispatcherServlet 。
4.2 TomcatHttpBinder
com.alibaba.dubbo.remoting.http.tomcat.TomcatHttpBinder ,TomcatHttpServer 绑定器实现类。代码如下:
```plain text plain public class TomcatHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { return new TomcatHttpServer(url, handler); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
---
# 5. Jetty 实现
jetty 和 tomcat 包的实现,差不多,主要差异在 Tomcat 和 Jetty 的 API 不同。
所以,下面我们就贴贴代码啦,当然,还是有中文详细注释的。
## 5.1 JettyHttpServer
```plain text
plain public class JettyHttpServer extends AbstractHttpServer { private static final Logger logger = LoggerFactory.getLogger(JettyHttpServer.class); /** * 内嵌的 Jetty 服务器 */ private Server server; /** * URL 对象 */ private URL url; 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); // 注册 HttpHandler 到 DispatcherServlet 中 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); // 创建 Jetty Connector 对象 SelectChannelConnector connector = new SelectChannelConnector(); 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); // 添加 ServletContext 对象,到 ServletManager 中 // 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); ServletManager.getInstance().addServletContext(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), context.getServletContext()); // 启动 Jetty try { 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); } } @Override public void close() { // 标记关闭 super.close(); // 移除 ServletContext 对象 ServletManager.getInstance().removeServletContext(url.getParameter(Constants.BIND_PORT_KEY, url.getPort())); // 关闭 Jetty if (server != null) { try { server.stop(); } catch (Exception e) { logger.warn(e.getMessage(), e); } } } }
5.2 JettyHttpBinder
```plain text plain public class JettyHttpBinder implements HttpBinder { @Override public HttpServer bind(URL url, HttpHandler handler) { return new JettyHttpServer(url, handler); } }
1
2
3
4
5
6
7
8
9
10
11
---
# 6. Servlet Bridge 实现
## 6.1 ServletHttpServer
[com.alibaba.dubbo.remoting.http.servlet.ServletHttpServer](https://github.com/YunaiV/dubbo/blob/master/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java) ,实现 AbstractHttpServer 抽象类, 基于 Servlet 的服务器实现类。代码如下:
```plain text
plain public class ServletHttpServer extends AbstractHttpServer { public ServletHttpServer(URL url, HttpHandler handler) { super(url, handler); // 注册 HttpHandler 到 DispatcherServlet 中 DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, 8080), handler); } }
- 注意端口端口保持一致 ,在 配置的 ,和外部的 Servlet 容器的 , 。
- 需要配置 DispatcherServlet 到 web.xml 中。通过这样的方式,让外部的 Servlet 容器,可以进行转发。
6.2 ServletHttpBinder
```plain text plain public class ServletHttpBinder implements HttpBinder { @Adaptive() public HttpServer bind(URL url, HttpHandler handler) { return new ServletHttpServer(url, handler); } }
1
2
3
4
5
6
7
8
9
---
## 6.3 BootstrapListener
[com.alibaba.dubbo.remoting.http.servlet.BootstrapListener](https://github.com/YunaiV/dubbo/blob/master/dubbo-remoting/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/BootstrapListener.java) ,实现 ServletContextListener 接口, 启动监听器。代码如下:
```plain text
plain public class BootstrapListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { ServletManager.getInstance().addServletContext(ServletManager.EXTERNAL_SERVER_PORT, servletContextEvent.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { ServletManager.getInstance().removeServletContext(ServletManager.EXTERNAL_SERVER_PORT); } }
- 需要配置 BootstrapListener 到 web.xml 中。通过这样的方式,让外部的 ServletContext 对象,添加到 ServletManager 中。
666. 彩蛋
又开阔了下思路,美滋滋。另外,艿艿配置了 http:// 协议的例子,使用 Tomcat 内嵌服务器。地址如下:


