Tomcat 中的 NIO 源码分析
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; } } (编辑:云计算网_泰州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |