前面设计的Http服务通过异步处理业务,提高了IO线程的效率。但IO线程始终只有一个无法发挥现代服务器多核CPU并行处理能力。像Tomcat、Jobss 等生产级的Http 服务容器 ,会构造多个IO线程同时运行,每个IO线程中都独占一个Selector(选择器)。另外接收连接与IO读写也会分开,用专门线程处理OP_ACCEPT事件,工作内容包括:接收连接、验证连接安全性、检测连接是否超出限定值、将超出的数量添加至等待对列。
连接线程:调用accept()获得一个新管道,然后注册至 IO线程当中的选择器。请注意这里不能直接拿IO线程中的Selector进行注册,因为注册(register)和刷新(select)选择器是一个同步操作。如果在非IO线程中调用register,会导致阻塞发生。所以在Tomcat当中 ,会将新管道的注册操作封装成PollEvent(注册事件)添加至IO线程的任务对列。由IO线程在刷新之后执行。
- 具体代码详见:org.apache.tomcat.util.net.NioEndpoint
IO线程:
处理读取事件,并提交给业务线程池处理业务。Tomcat 这里的面的细节会很多,整个思路跟上面的Http服务是类似的,就不在这里赘述。
总结
- 基于心跳服务我们学到 选持器的监听事件是可以进轮转的。
- 基于TCP的事件必须进行消费,否则会一直触发该事件,造成死循环
- 耗时操作不能在IO线程中完成,因为所有的IO操作都是同步的。
- 为了更高效利用服务器多核CPU特性, IO线程可以有多个,并且都有独立的选择器
- 所有的IO操作都必须在IO线程完成(如注册、刷新),否则会导致资源锁争抢和阻塞。