文将介绍 Tomcat 中的 NIO 使用,使大家对 Java NIO 的生产使用有更加直观的认识。
虽然本文的源码篇幅也不短,但是 Tomcat 的源码毕竟不像 Doug Lea 的并发源码那么“变态”,对于大部分读者来说,阅读难度比之前介绍的其他并发源码要简单一些,所以读者不要觉得有什么压力。
本文基于 Tomcat 当前(2018-03-20)最新版本 9.0.6。
先简单画一张图示意一下本文的主要内容:
目录
源码环境准备
endpoint
init 过程分析
start 过程分析
Acceptor
Poller
processKey
总结
源码环境准备
Tomcat 9.0.6 下载地址:https://tomcat.apache.org/download-90.cgi
由于上面下载的 tomcat 的源码并没有使用 maven 进行组织,不方便我们看源码,也不方便我们进行调试。这里我们将使用 maven 仓库中的 tomcat-embed-core,自己编写代码进行启动的方式来进行调试。
首先,创建一个空的 maven 工程,然后添加以下依赖。
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-coreartifactId>
<version>9.0.6version>
dependency>
上面的依赖,只会将 tomcat-embed-core-9.0.6.jar 和 tomcat-annotations-api-9.0.6.jar 两个包引进来,对于本文来说,已经足够了,如果你需要其他功能,需要额外引用其他的依赖,如 Jasper。
然后,使用以下启动方法:
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
Connector connector = new Connector("HTTP/1.1");
connector.setPort(8080);
tomcat.setConnector(connector);
tomcat.start();
tomcat.getServer().await();
}
经过以上的代码,我们的 Tomcat 就启动起来了。
Tomcat 中的其他接口感兴趣的读者请自行探索,如设置 webapp 目录,设置 resources 等
这里,介绍第一个重要的概念:Connector。在 Tomcat 中,使用 Connector 来处理连接,一个 Tomcat 可以配置多个 Connector,分别用于监听不同端口,或处理不同协议。
在 Connector 的构造方法中,我们可以传 HTTP/1.1 或 AJP/1.3 用于指定协议,也可以传入相应的协议处理类,毕竟协议不是重点,将不同端口进来的连接对应不同处理类才是正道。典型地,我们可以指定以下几个协议处理类:
org.apache.coyote.http11.Http11NioProtocol:对应非阻塞 IO
org.apache.coyote.http11.Http11Nio2Protocol:对应异步 IO
org.apache.coyote.http2.Http2Protocol:对应 http2 协议,对 http2 感兴趣的读者,赶紧看起来吧。
本文的重点当然是非阻塞 IO 了,之前已经介绍过异步 IO的基础知识了,读者看完本文后,如果对异步 IO 的处理流程感兴趣,可以自行去分析一遍。
如果你使用 9.0 以前的版本,Tomcat 在启动的时候是会自动配置一个 connector 的,我们可以不用显示配置。
9.0 版本的 Tomcat#start() 方法:
public void start() throws LifecycleException {
getServer();
server.start();
}
8.5 及之前版本的 Tomcat#start() 方法:
public void start() throws LifecycleException {
getServer();
// 自动配置一个使用非阻塞 IO 的 connector
getConnector();
server.start();
}
endpoint
前面我们说过一个 Connector 对应一个协议,当然这描述也不太对,NIO 和 NIO2 就都是处理 HTTP/1.1 的,只不过一个使用非阻塞,一个使用异步。进到指定 protocol 代码,我们就会发现,它们的代码及其简单,只不过是指定了特定的 endpoint。
打开 Http11NioProtocol 和 Http11Nio2Protocol源码,我们可以看到,在构造方法中,它们分别指定了 NioEndpoint 和 Nio2Endpoint。
// 非阻塞模式
public class Http11NioProtocol extends AbstractHttp11JsseProtocol {
public Http11NioProtocol() {
// NioEndpoint
super(new NioEndpoint());
}
...
}
// 异步模式
public class Http11Nio2Protocol extends AbstractHttp11JsseProtocol {
public Http11Nio2Protocol() {
// Nio2Endpoint
super(new Nio2Endpoint());
}
...
}
作者:慕容千语
链接:https://www.jianshu.com/p/c6d57441da5b
共同学习,写下你的评论
评论加载中...
作者其他优质文章