加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 服务器 > 系统 > 正文

Tomcat 中的 NIO 源码分析

发布时间:2020-01-09 04:15:03 所属栏目:系统 来源:站长网
导读:副标题#e# 虽然本文的源码篇幅也不短,但是 Tomcat 的源码毕竟不像 Doug Lea 的并发源码那么变态,对于大部分读者来说,阅读难度比之前介绍的其他并发源码要简单一些,所以读者不要觉得有什么压力。 本文基于 Tomcat 当前(2018-03-20)最新版本 9.0.6。 先简

NioEndpoint 继承 => AbstractJsseEndpoint 继承 => AbstractEndpoint。中间的 AbstractJsseEndpoint 主要是提供了一些关于 HTTPS的方法,这块我们暂时忽略它,后面所有关于 HTTPS 的我们都直接忽略,感兴趣的读者请自行分析。

init 过程分析

下面,我们看看从 tomcat.start 一直到 NioEndpoint 的过程。

1. AbstractProtocol # init

@Override 

public voidinit throws Exception { 

... 

String endpointName = getName; 

endpoint.setName(endpointName.substring(, endpointName.length-1 1)); 

endpoint.setDomain(domain); 

// endpoint 的 name=http-nio-8089,domain=Tomcat 

endpoint.init; 

2. AbstractEndpoint # init

public final void init throws Exception { 

if (bindOnInit) { 

bind; // 这里对应的当然是子类 NioEndpoint 的 bind 方法 

bindState = BindState.BOUND_ON_INIT; 

... 

3. NioEndpoint # bind

这里就到我们的 NioEndpoint 了,要使用到我们之前学习的 NIO 的知识了。

@Override 

public voidbind throws Exception { 

// initServerSocket; 原代码是这行,我们 “内联” 过来一起说 

 

// 开启 ServerSocketChannel 

serverSock = ServerSocketChannel.open; 

socketProperties.setProperties(serverSock.socket); 

 

// getPort 会返回我们最开始设置的 8080,得到我们的 address 是 0.0.0.0:8080 

InetSocketAddress addr = (getAddress!=?new InetSocketAddress(getAddress,getPort):new InetSocketAddress(getPort)); 

 

// ServerSocketChannel 绑定地址、端口, 

// 第二个参数 backlog 默认为 100,超过 100 的时候,新连接会被拒绝(不过源码注释也说了,这个值的真实语义取决于具体实现) 

serverSock.socket.bind(addr,getAcceptCount); 

 

// ※※※ 设置 ServerSocketChannel 为阻塞模式 ※※※ 

serverSock.configureBlocking(true); 

 

// 设置 acceptor 和 poller 的数量,至于它们是什么角色,待会说 

// acceptorThreadCount 默认为 1 

if (acceptorThreadCount == 0) { 

// FIXME: Doesn't seem to work that well with multiple accept threads 

// 作者想表达的意思应该是:使用多个 acceptor 线程并不见得性能会更好 

acceptorThreadCount = 1; 

 

// poller 线程数,默认值定义如下,所以在多核模式下,默认为 2 

// pollerThreadCount = Math.min(2,Runtime.getRuntime.availableProcessors); 

if (pollerThreadCount <= 0) { 

pollerThreadCount = 1; 

 

// 

setStopLatch(new CountDownLatch(pollerThreadCount)); 

 

// 初始化 ssl,我们忽略 ssl 

initialiseSsl; 

 

// 打开 NioSelectorPool,先忽略它 

selectorPool.open; 

ServerSocketChannel 已经打开,并且绑定要了之前指定的 8080 端口,设置成了阻塞模式。

设置了 acceptor 的线程数为 1

设置了 poller 的线程数,单核 CPU 为 1,多核为 2

打开了一个 SelectorPool,我们先忽略这个

到这里,我们还不知道 Acceptor 和 Poller 是什么东西,我们只是设置了它们的数量,我们先来看看最后面提到的 SelectorPool。

start 过程分析

刚刚我们分析完了 init 过程,下面是启动过程 start 分析。

AbstractProtocol # start

@Override 

public voidstart throws Exception { 

... 

// 调用 endpoint 的 start 方法 

endpoint.start; 

 

// Start async timeout thread 

asyncTimeout = new AsyncTimeout; 

Thread timeoutThread = new Thread(asyncTimeout, getNameInternal + "-AsyncTimeout"); 

int priority = endpoint.getThreadPriority; 

if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { 

priority = Thread.NORM_PRIORITY; 

timeoutThread.setPriority(priority); 

timeoutThread.setDaemon(true); 

timeoutThread.start; 

AbstractEndpoint # start

public final void start throws Exception { 

// 按照我们的流程,刚刚 init 的时候,已经把 bindState 改为 BindState.BOUND_ON_INIT 了, 

// 所以下面的 if 分支我们就不进去了 

if (bindState == BindState.UNBOUND) { 

bind; 

bindState = BindState.BOUND_ON_START; 

// 往里看 NioEndpoint 的实现 

startInternal; 

下面这个方法还是比较重要的,这里会创建前面说过的 acceptor 和 poller。

NioEndpoint # startInternal

@Override 

public voidstartInternal throws Exception { 

 

if (!running) { 

running = true; 

paused = false; 

 

// 以下几个是缓存用的,之后我们也会看到很多这样的代码,为了减少 new 很多对象出来 

processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, 

socketProperties.getProcessorCache); 

eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, 

socketProperties.getEventCache); 

nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, 

socketProperties.getBufferPool); 

 

// 创建【工作线程池】,Tomcat 自己包装了一下 ThreadPoolExecutor, 

// 1. 为了在创建线程池以后,先启动 corePoolSize 个线程(这个属于线程池的知识了,不熟悉的读者可以看看我之前的文章) 

// 2. 自己管理线程池的增长方式(默认 corePoolSize 10, maxPoolSize 200),不是本文重点,不分析 

if ( getExecutor == ) { 

createExecutor; 

 

// 设置一个栅栏(tomcat 自定义了类 LimitLatch),控制最大的连接数,默认是 10000 

initializeConnectionLatch; 

 

// 开启 poller 线程 

// 还记得之前 init 的时候,默认地设置了 poller 的数量为 2,所以这里启动 2 个 poller 线程 

pollers = new Poller[getPollerThreadCount()]; 

for (int i=0; i<pollers.length; i++) { 

pollers[i] = new Poller; 

Thread pollerThread = new Thread(pollers[i], getName + "-ClientPoller-"+i); 

pollerThread.setPriority(threadPriority); 

pollerThread.setDaemon(true); 

pollerThread.start; 

 

// 开启 acceptor 线程,和开启 poller 线程组差不多。 

// init 的时候,默认地,acceptor 的线程数是 1 

startAcceptorThreads; 

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读