继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java编程架构详解——Tomcat 中的 NIO 源码分析

慕村9548890
关注TA
已关注
手记 1281
粉丝 227
获赞 991

文将介绍 Tomcat 中的 NIO 使用,使大家对 Java NIO 的生产使用有更加直观的认识。

虽然本文的源码篇幅也不短,但是 Tomcat 的源码毕竟不像 Doug Lea 的并发源码那么“变态”,对于大部分读者来说,阅读难度比之前介绍的其他并发源码要简单一些,所以读者不要觉得有什么压力。

本文基于 Tomcat 当前(2018-03-20)最新版本 9.0.6

先简单画一张图示意一下本文的主要内容:

webp

目录

源码环境准备

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


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP