Netty Reactor模型

1、单线程

时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一。

在Java领域当时比较流行的是单线程编程,对于CPU密集型的应用程序而言,频繁的通过多线程进行协作和抢占时间片反而会降低性能。

2、多线程

随着硬件性能的提升,CPU的核数越来越越多,很多服务器标配已经达到32或64核。通过多线程并发编程,可以充分利用多核CPU的处理能力,提升系统的处理效率和并发性能。

 

从2005年开始,随着多核处理器的逐步普及,java的多线程并发编程也逐渐流行起来,当时商用主流的JDK版本是1.4,用户可以通过 new Thread()的方式创建新的线程。

由于JDK1.4并没有提供类似线程池这样的线程管理容器,多线程之间的同步、协作、创建和销毁等工作都需要用户自己实现。由于创建和销毁线程是个相对比较重量级的操作,因此,这种原始的多线程编程效率和性能都不高。

3、线程池

为了提升Java多线程编程的效率和性能,降低用户开发难度。JDK1.5推出了java.util.concurrent并发编程包。在并发编程类库中,提供了线程池、线程安全容器、原子类等新的类库,极大的提升了Java多线程编程的效率,降低了开发难度。

 

从JDK1.5开始,基于线程池的并发编程已经成为Java多核编程的主流。

4、Reactor模型

无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件。

 

4.1 单线程模型

Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:

1)作为NIO服务端,接收客户端的TCP连接;

2)作为NIO客户端,向服务端发起TCP连接;

3)读取通信对端的请求或者应答消息;

4)向通信对端发送消息请求或者应答消息。

示意图如下:

由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作。从架构层面看,一个NIO线程确实可以完成其承担的职责。例如,通过Acceptor类接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户线程可以通过消息编码通过NIO线程将消息编码发送给客户端

注意:对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:

1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;

2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;

3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

为了解决这些问题,演进出了Reactor多线程模型,下面我们一起学习下Reactor多线程模型。

5、多线程模型

Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操作,它的原理图如下: 

 

Reactor多线程的特点:

1)有一个专门的Reator线程-Acceptor线程监听服务器端,接收客户端的TCP请求。

2)网络IO操作读写有一个NIO线程池负责,线程池采用标准的jdk线程实现,包括了一个任务队列和N个可用的线程,由他们负责消息读取、编码、解码、和发送给客户端。

3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程。
 

 

猜你喜欢

转载自hpgary.iteye.com/blog/2384274