曹工说Tomcat:200个http-nio-8080-exec线程全都被第三方服务拖住了,这可如何是好(上:线程模型解析) (2)

既然丢到队列了,那我们看看什么时候去队列取的呢?

@Override public void run() { // Loop until destroy() is called while (true) { boolean hasEvents = false; // 检查events hasEvents = events(); } }

这里我们跟一下events()。

public boolean events() { boolean result = false; PollerEvent pe = null; for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) { result = true; pe.run(); ... } return result; }

这里的

pe = events.poll()

就是去队列拉取事件,拉取到了之后,就会赋值给pe,然后下面就调用了pe.run方法。

pe的类型是PollerEvent,我们看看其run方法会干啥?

@Override public void run() { if (interestOps == OP_REGISTER) { try { socket.getIOChannel().register(socket.getSocketWrapper().getPoller().getSelector(), SelectionKey.OP_READ, socket.getSocketWrapper()); } catch (Exception x) { log.error(sm.getString("endpoint.nio.registerFail"), x); } } }

这个方法难理解吗,看着有点吓人,其实就是把这个新的连接,向selector注册,感兴趣的io事件为OP_READ。后续呢,这个连接的io读写,就全由本poller的selector包了。

tomcat如何处理客户端读事件

我们说了,poller是个线程,在其runnable实现里,除了要处理上面的新连接注册到selector这个事,还要负责io读写,这部分逻辑就是在:

Iterator<SelectionKey> iterator=selector.selectedKeys().iterator(); while (iterator != null && iterator.hasNext()) { SelectionKey sk = iterator.next(); NioSocketWrapper socketWrapper = sk.attachment(); processKey(sk, socketWrapper); }

最后一行的processKey,会调用如下逻辑,将工作甩锅给http-nio-8080-exec-2@5076这类打杂的线程。

public boolean processSocket(SocketWrapperBase<S> socketWrapper,SocketEvent event, boolean dispatch) { Executor executor = getExecutor(); executor.execute(sc); return true; }

给个图的话,大概就是如下的红线流程部分了:

曹工说Tomcat:200个http-nio-8080-exec线程全都被第三方服务拖住了,这可如何是好(上:线程模型解析)

小结

好了,到了课后思考时间了,我们也说了,最终会交给http-nio-8080-exec-2@5076这类线程所在的线程池,那假设这些线程全都在sleep,会发生什么呢?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyppx.html