我在前面的专栏里介绍了 Jetty 的总体架构设计,简单回顾一下,Jetty 总体上是由一系列 Connector、一系列 Handler 和一个 ThreadPool 组成,它们的关系如下图所示:
相比较 Tomcat 的连接器,Jetty 的 Connector 在设计上有自己的特点。Jetty 的 Connector 支持 NIO 通信模型,我们知道 NIO 模型中的主角就是 Selector,Jetty 在 Java 原生 Selector 的基础上封装了自己的 Selector,叫作 ManagedSelector。ManagedSelector 在线程策略方面做了大胆尝试,将 I/O 事件的侦测和处理放到同一个线程来处理,充分利用了 CPU 缓存并减少了线程上下文切换的开销。
具体的数字是,根据 Jetty 的官方测试,这种名为“EatWhatYouKill”的线程策略将吞吐量提高了 8 倍。你一定很好奇它是如何实现的吧,今天我们就来看一看这背后的原理是什么。
Selector 编程的一般思路
常规的 NIO 编程思路是,将 I/O 事件的侦测和请求的处理分别用不同的线程处理。具体过程是:
启动一个线程,在一个死循环里不断地调用 select 方法,检测 Channel 的 I/O 状态,一旦 I/O 事件达到,比如数据就绪,就把该 I/O 事件以及一些数据包装成一个 Runnable,将 Runnable 放到新线程中去处理。