学习Netty的过程中,说Netty的线程模型就是基于Reactor模型的一个实现。因此,这里对Reactor模型进行了简单的学习,并进行了一些整理。
在web服务中,处理web请求通常有两种体系结构,分别为:thread-based architecture(基于线程的架构)、event-driven architecture(事件驱动模型)。
thread-based architecture(基于线程的架构):
多线程并发模式:一个连接一个线程,服务器每当收到客户端的一个请求, 便开启一个独立的线程来处理。前面nio的文章中BIO的多线程实现方式就是这种。
event-driven architecture(事件驱动模型):
事件驱动体系结构是目前比较广泛使用的一种。这种方式会定义一系列的事件处理器来响应事件的发生,并且将服务端接受连接与对事件的处理分离。其中,事件是一种状态的改变。比如,tcp中socket的new incoming connection、ready for read、ready for write。
Reactor模式和Proactor模式都是是event-driven architecture(事件驱动模型)的实现方式。
Reactor主要分为几个组件:分别是Reactor,Acceptor,Handler。
Reactor组件负责分发事件,如果是连接事件,那么交给Acceptor,如果是读写事件,那么交给Handler。
Acceptor负责处理连接事件(获取新连接,注册到Selector上,注册读写事件,绑定Handler)。
Handler负责处理读写事件(使用channel进行读写)。
而根据Reactor的数量和线程池的数量,又将Reactor分为三种模型:
单线程模型(单Reactor单线程)
多线程模型(单Reactor多线程)
主从多线程模型(多Reactor多线程)
单线程模型(单Reactor单线程):
Reactor内部通过selector监控事件,收到事件后通过dispatch进行分发,如果是连接建立的事件,则由Acceptor处理,Acceptor通过accept接受连接,并创建一个Handler来处理连接后续的各种事件,如果是读写事件,直接调用连接对应的Handler来处理。
关键部分代码示例(https://www.jianshu.com/p/eb28811421e3):
1.Reactor内部通过selector监控事件,收到事件后通过dispatch进行分发。
2.dispatch中如果是连接建立的事件,则由Acceptor处理,如果是读写事件,直接调用连接对应的Handler来处理。
这里需要解释一下,通过传入的key的attachment方法获取到对应的Acceptor或Handler(代码中的Processor),因为Acceptor或Handler(代码中的Processor)类都实现了Runnable接口。然后调用Acceptor或Handler的run方法,这样就可以做到事件转发了。
3.Acceptor通过accept接受连接,并创建一个Handler(代码中的Processor)来处理连接后续的各种事件。
上面是典型的单线程版本的Reactor实现,实例化Reactor对象的过程中,在当前多路复用器Selector上注册了OP_ACCEPT事件,当OP_ACCEPT事件发生后,Reactor通过dispatch方法执行Acceptor的run方法,Acceptor类的主要功能就是接受请求,建立连接,并将代表连接建立的SocketChannel以参数的形式构造Handle(Processor)对象。
4.Handler(Processor)来处理对应channel的读写事件。
Handler完成read->(decode->compute->encode)->send的业务流程。
注:
上面的Acceptor和Handler虽然都实现了Runnable接口,但是在调用时都是直接调用的run方法,不是多线程的。
优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成。
缺点:性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈
缺点:可靠性问题,线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障 。
多线程模型(单Reactor多线程):
主线程中,Reactor对象通过selector监控事件,收到事件后通过dispatch进行分发,如果是连接建立事件,则由Acceptor处理,Acceptor通过accept接收连接,并创建一个Handler来处理后续事件,而Handler只负责响应事件,不进行业务操作,也就是只进行read读取数据和write写出数据,业务处理交给一个线程池进行处理。
线程池分配一个线程完成真正的业务处理,然后将响应结果交给主进程的Handler处理,Handler将结果send给client。
单Reactor承当所有事件的监听和响应,而当我们的服务端遇到大量的客户端同时进行连接,或者在请求连接时执行一些耗时操作,比如身份认证,权限检查等,这种瞬时的高并发就容易成为性能瓶颈。
主从多线程模型(多Reactor多线程):
Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件 。
当 Acceptor处理连接事件后,MainReactor 将连接(socketChannel)分配给SubReactor
SubReactor将连接加入到连接队列进行监听,并创建handler进行各种事件处理。SubReactor有自己的Select,负责对应的读写事件等。
当有新事件发生时,SubReactor就会调用对应的handler处理
handler通过read 读取数据,分发给后面的worker 线程处理
worker线程池分配独立的worker 线程进行业务处理,并返回结果
handler收到响应的结果后,再通过send 将结果返回给client
Reactor主线程可以对应多个Reactor子线程, 即MainRecator 可以关联多个SubReactor。
代码示例(实现了一个简单的主从多线程模型,一个MainReactor ,MainReactor 有一个主selector,专门维护accept事件,当接收到accept事件,交给SubReactor(也是一个),SubReactor有一个单独的从selector,从selector维护该连接的read和write事件):
https://www.cnblogs.com/eason-ou/p/11912010.html