概述
- 在使用JAVA NIO和多线程来进行高并发Java服务端应用程序设计时,通常是基于Reactor线程模型来设计的。Reactor,即包含一个Java NIO的多路复用选择器Selector的反应堆,当有反应时,即该Selector所管理的某个客户端连接有IO事件过来时,则在当前线程或者分配到其他线程来处理该IO事件。
- Reactor线程模型通常由接收客户端连接请求的acceptor线程和处理客户端的IO请求的IO线程两部分组成,而acceptor线程和IO处理线程可以是同一个线程,也可以是不同的线程或者是各自对应一个线程池。
单线程Reactor模型
- 单线程Reactor模型是指由一个线程绑定一个Java NIO的多路复用选择器Selector,由该线程来处理该Selector的所有IO事件,包括新客户端连接建立请求,即监听套接字的IO事件,和已经建立连接的客户端套接字的所有IO事件请求,如数据读写。在单线程Reactor模型中,Reactor模型中的新客户端连接建立的acceptor线程和已经建立连接的客户端套接字的IO请求处理的IO线程是同一个线程。
- 具体工作模式如图:服务端使用一个线程来处理新客户端的连接请求和已建立连接的客户端的读写IO事件。
- 由于该Selector所管理的所有客户端连接和服务端的监听套接字的IO请求都是由该线程来处理,所以如果当前线程正在处理某个客户端的数据读写IO请求,则无法处理当前的新建立连接的客户端请求,导致客户端无法建立连接或者连接超时。同时由于只使用一个线程所有也没有充分利用多核CPU。
- 所以虽然通过使用单线程,规避了线程竞争和线程上下文切换,但是由于单个线程处理能力有限,所以单线程Reactor模型也无法支撑高并发客户端请求的场景,Java服务端应用程序设计很少使用到该模型。
单Acceptor线程多IO线程的Reactor模型
- 多线程Reactor模型与单线程Reactor模型的主要区别是已经建立连接的客户端套接字的IO事件是在另外一个线程或者另外一个线程池来处理的,这种线程也称为IO线程,而处理监听套接字的新客户端连接请求的线程则还是一个独立的Acceptor线程。
- 具体工作过程如图所示:使用在一个独立Acceptor线程通过监听套接字监听客户端的连接请求,当有新的客户端连接到来时,创建该客户端对应的channel并注册到其他一个IO线程的Selector,由该Selector监视和获取该客户端channel后续的读写IO事件并进行处理。
- 以上示意图是针对一个已建立连接的客户端套接字的所有IO请求可以是始终在IO线程池中的一个IO线程中处理。不过也可以是使用一个专门的IO线程来负责监视所有客户端的channels,当某个客户端有IO事件到来时,将该客户端的此次IO事件封装为一个Runnable的任务,交给另外的线程池处理,即某个客户端的所有IO事件会在不同中线程处理。不过推荐是始终在一个IO线程中处理,这样就不存在多个线程对该客户端套接字进行并发操作的场景,即不需要通过加锁之类的操作来对该客户端套接字对应的channel对象引用进行线程同步。
- 这种已建立连接的客户端的所有IO事件都是在同一个IO线程中处理的方式也是netty4的IO线程模型,通过客户端channel与线程的绑定来避免线程竞争来提高性能。
多Acceptor线程多IO线程Reactor模型
- 多Acceptor线程多IO线程Reactor模型实际与单Acceptor线程多IO线程Reactor模型差不多,主要一个区别就是用于接收客户端的连接请求的线程不再是只使用一个acceptor线程,而是使用一个包含多个acceptor线程的线程池,从而解决单个acceptor线程在处理多个不同的端口的高并发客户端连接建立请求时的性能瓶颈,即当服务端需要同时监听多个端口时,可以使用一个包含多个Acceptor线程的线程池。
- 该模型的工作过程如图所示:使用多个Acceptor线程来处理客户端的连接请求。