基本上所有的网络处理程序都有以下基本的处理过程:
- Read request
- Decode request
- Process service
- Encode reply
- Send reply
Classic Service Designs(传统服务设计BIO)
对于每一个请求都分发给一个线程,每个线程中都独自处理上面的流程。
这种模型由于IO在阻塞时会一直等待,因此在用户负载增加时,性能下降的非常快。
server导致阻塞的原因:
1、serversocket的accept方法,阻塞等待client连接,直到client连接成功。
2、线程从socket inputstream读入数据,会进入阻塞状态,直到全部数据读完。
3、线程向socket outputstream写入数据,会阻塞直到全部数据写完。
client导致阻塞的原因:
1、client建立连接时会阻塞,直到连接成功。
2、线程从socket输入流读入数据,如果没有足够数据读完会进入阻塞状态,直到有数据或者读到输入流末尾。
3、线程从socket输出流写入数据,直到输出所有数据。
4、socket.setsolinger()设置socket的延迟时间,当socket关闭时,会进入阻塞状态,直到全部数据都发送完或者超时。
改进:采用基于事件驱动的设计,当有事件触发时,才会调用处理器进行数据处理。
Basic Reactor Design(响应式Reactor设计NIO)
1、Reactor单线程模型,
Reactor负责多路分离套接字,Accept新连接,并分派请求到处理器链路中。该模型适用于处理器链中业务处理组件能快速完成的场景,不过,这种单线程模型不能充分利用多核资源,所以使用不多。
改进:使用多线程处理业务逻辑。
2、worker Thread Pools(worker 线程池)
相比较上一种模型,该模型在处理器链部分采用多线程(线程池),也是后端程序常用的模型
将处理器的执行放入线程池,多线程进行业务处理。但Reactor仍为单个线程。
继续改进:对于多个CPU的机器,为充分利用系统资源,将Reactor拆分为两部分。
3、Using Multiple Reactors(使用多个Reactor)
第三种模型比第二种模型,是将Reactor分成两部分,mainReactor负责监听server socket,accept新连接,并将建立的socket分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据,对业务处理功能,其扔给worker线程池完成。
说完 Reactor 模型的几种形式,那么Netty是哪种呢?其实,我还有一种Reactor模型的变种没说,那就是去掉线程池的第三种形式的变种,这也 是Netty NIO的默认模式。在实现上,Netty中的Boss类充当mainReactor,NioWorker 类充当subReactor(默认 NioWorker 的个数是Runtime.getRuntime().availableProcessors()*2)。在处理新来的请求 时,NioWorker 读完已收到的数据到 ChannelBuffer 中,之后触发ChannelPipeline 中的 ChannelHandler 流。