Tomcat 中的 NIO 源码分析
到这里,我们启动了工作线程池、 poller 线程组、acceptor 线程组。同时,工作线程池初始就已经启动了 10 个线程。我们用 jconsole 来看看此时的线程,请看下图: 从 jconsole 中,我们可以看到,此时启动了 BlockPoller、worker、poller、acceptor、AsyncTimeout,大家应该都已经清楚了每个线程是哪里启动的吧。 Tomcat 中并没有 Worker 这个类,此名字是我瞎编。 此时,我们还是不知道 acceptor、poller 甚至 worker 到底是干嘛的,下面,我们从 acceptor 线程开始看起。 Acceptor 它的结构非常简单,在构造函数中,已经把 endpoint 传进来了,此外就只有 threadName 和 state 两个简单的属性。 private final AbstractEndpoint<?,U> endpoint; private String threadName; protected volatile AcceptorState state = AcceptorState.NEW;
publicAcceptor(AbstractEndpoint<?,U> endpoint) { this.endpoint = endpoint; } threadName 就是一个线程名字而已,Acceptor 的状态 state 主要是随着 endpoint 来的。 public enum AcceptorState { NEW, RUNNING, PAUSED, ENDED } 我们直接来看 acceptor 的 run 方法吧: Acceptor # run @Override public voidrun {
int errorDelay = 0;
// 只要 endpoint 处于 running,这里就一直循环 while (endpoint.isRunning) {
// 如果 endpoint 处于 pause 状态,这边 Acceptor 用一个 while 循环将自己也挂起 while (endpoint.isPaused && endpoint.isRunning) { state = AcceptorState.PAUSED; try { Thread.sleep(50); } catch (InterruptedException e) { // Ignore } } // endpoint 结束了,Acceptor 自然也要结束嘛 if (!endpoint.isRunning) { break; } state = AcceptorState.RUNNING;
try { // 如果此时达到了最大连接数(之前我们说过,默认是10000),就等待 endpoint.countUpOrAwaitConnection;
// Endpoint might have been paused while waiting for latch // If that is the case, don't accept new connections if (endpoint.isPaused) { continue; }
U socket = ; try { // 这里就是接收下一个进来的 SocketChannel // 之前我们设置了 ServerSocketChannel 为阻塞模式,所以这边的 accept 是阻塞的 socket = endpoint.serverSocketAccept; } catch (Exception ioe) { // We didn't get a socket endpoint.countDownConnection; if (endpoint.isRunning) { // Introduce delay if necessary errorDelay = handleExceptionWithDelay(errorDelay); // re-throw throw ioe; } else { break; } } // accept 成功,将 errorDelay 设置为 0 errorDelay = 0;
if (endpoint.isRunning && !endpoint.isPaused) { // setSocketOptions 是这里的关键方法,也就是说前面千辛万苦都是为了能到这里进行处理 if (!endpoint.setSocketOptions(socket)) { // 如果上面的方法返回 false,关闭 SocketChannel endpoint.closeSocket(socket); } } else { // 由于 endpoint 不 running 了,或者处于 pause 了,将此 SocketChannel 关闭 endpoint.destroySocket(socket); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); String msg = sm.getString("endpoint.accept.fail"); // APR specific. // Could push this down but not sure it is worth the trouble. if (t instanceof Error) { Error e = (Error) t; if (e.getError == 233) { // Not an error on HP-UX so log as a warning // so it can be filtered out on that platform // See bug 50273 log.warn(msg, t); } else { log.error(msg, t); } } else { log.error(msg, t); } } } state = AcceptorState.ENDED; } 大家应该发现了,Acceptor 绕来绕去,都是在调用 NioEndpoint 的方法,我们简单分析一下这个。 (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |