其他分享
首页 > 其他分享> > 高性能网络框架笔记四(IO线程模型)

高性能网络框架笔记四(IO线程模型)

作者:互联网

上一文介绍中,我们详述了网络数据包的接收和发送过程,并通过介绍5中IO模型了解了内核是如何读取网络数据并通知给用户线程的。

前面的内容都是以内核空间的视角来剖析网络数据的收发模型,本小节我们站在用户空间的视角来看一下如何对网络数据进行收发。

相对内核来讲,用户空间的IO线程模型相对简单一些。这些用户空间的IO线程模型都是在讨论当前多线程一起配合工作时谁负责接收连接,谁负责响应IO读写,谁负责计算,谁负责发送和接收,仅仅是用户IO线程的不同分工模式。

1、Reactor

Reactor是利用NIO对IO线程进行不同的分工:

通过IO多路复用技术就可以不断的监听IO事件,不断的分发dispatch,就像一个反应堆一样,看起来像不断的产生IO事件,因此我们称这种模式为Reactor模式。

Reactor模型分三类:

1.1 单Reactor单线程

Reactor是依赖IO多路复用技术实现监听IO事件,从而源源不断的产生IO就绪事件,在Linux系统下我们使用epoll来进行IO多路复用,我们以Linux系统为例:

单Reactor单线程模型就好比我们开了个很小的饭馆,作为老板的我们需要一个人看所有的事情,包括:迎接顾客(accept事件),为顾客介绍菜单并等待顾客点菜(IO请求),做菜(业务处理),上菜(IO响应),送客(断开连接)

1.2单Reactor多线程

随着客人的增多(并发请求),显然饭馆只有一个人(单线程)干活肯定忙不过来,这时招聘一些员工(多线程)来帮忙干上述事情。

于是就有了单Reactor多线程模型:

1.3主从Reactor多线程

做任何事情都要区分事件的优先级,我们应该优先高效的去做优先级更高的事情,而不是一股脑儿不分优先级的全部去做。

当我们的小饭馆客人越来越多(并发量越来越大),我们就需要扩大饭店的规模,在这个过程中我们发现,迎接客人是饭店最重要的工作,我们要把客人迎进来,不能让客人一看人多就走掉,只要客人进来了,哪怕菜做的慢一点也没关系。

注意:这里想从Reactor注册的只是read事件,并没有注册write事件,因为read事件是有epoll内核触发的,而write事件则是由用户业务线程触发的(什么时候发送数据是由具体业务线程决定的),所以write事件理应由用户业务线程去注册。

用户线程注册write事件的时机是只有当用户发送的数据无法一次性全部写入buffer时,才会去注册write事件,等待buffer重新科协时,继续写入剩下的发送数据,如果用哪个好线程一股脑的将发送数据全部写入buffer,那么也就无需注册write事件到从Reactor中。

主从Reactor多线程模型是目前大部分主流网络框架中采用的一种IO线程模型。Netty就是用的这种模型。

2、Preactor

Proactor是基于AIO对IO线程进行分工的一种模型。前面我们介绍了异步IO模型,它是操作系统内核支持的一种全异步编程模型,在数据准备阶段和数据拷贝阶段全程无阻塞。

Proactor线程模型将IO事件的监听,IO操作的执行,IO结果的dispatch统统交给内核来做。

Proactor模型组件介绍:

Proactor执行过程:

在Proactor中我们关系的IO完成事件:内核已经帮助我们读好数据并放入我们指定的读缓冲区,用户线程可以直接读取。在Reactor中我们中我们关系的是IO就绪事件:数据已经到来,但是需要用户线程自己去内核读取。

3、Reactor与Proactor对比

4、Netty的IO模型

介绍完网络数据包在内核中的收发过程以及五种IO模型和两种线程模型后,线程我们来看下Netty中的IO模型是什么样的。

在我们介绍Reactor IO线程模型的时候提到有三种Reactor模型:单Reactor单线程,单Reactor多线程,主从Reactor多线程。

这三种Reactor模型在netty中都是支持的,但是我们常用的是主从Reactor多线程模型。

而我们之前介绍的三种Reactor只是一种模型,是一种设计思想。实际上各种网络框架在实现中并不是严格按照模型来实现的,会有一些小的不同,但大体设计思想上是一样的。

下面我们来看下netty中的主从Reactor多线程模型是什么样子:

MainReactorGroup中只有一个Reactor的原因是,通常我们的服务端程序只会绑定监听一个端口,如果绑定监听多个端口,就会配置多个Reactor。

socket连接在创建后就被固定的分配给一个Reactor,所以一个socket连接也只会被一个固定的IO线程执行,每个socket连接分配一个独立的PipeLine实例,用来编排这个socket连接上的IO处理逻辑。这种无锁串行化的设计目的是为了防止线程并发执行同一个socket连接上的IO逻辑处理,防止出现线程安全问题。同时使系统吞吐量达到最大化

由于每个Reactor中只有一个IO线程,这个IO线程叫执行IO活跃的socket连接对应的Pipeline中的ChannelHandler,又要从Reactor中获取IO就绪事件,执行IO调用。所以Pipeline中channelhandler中执行的逻辑不能耗时太长,尽量将耗时的业务逻辑处理放入单独的业务线程池中处理,否则会影响其它连接的IO读写,从而近一步影响到整个服务程序的IO吞吐。

当IO请求在业务线程中完成相应的业务逻辑处理后,在业务线程中利用持有的ChannelHandlerContext引用将响应数据在Pipeline中反向传播,最终写回给客户端。

netty支持的三种Reactor模型:

配置单Reactor单线程

EventLoopGroup eventGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap(); 
serverBootstrap.group(eventGroup);

配置单Reactor多线程

EventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap(); 
serverBootstrap.group(eventGroup);

配置主从Reactor多线程

EventLoopGroup bossGroup = new NioEventLoopGroup(1); 
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap(); 
serverBootstrap.group(bossGroup, workerGroup);

 

https://mp.weixin.qq.com/s/Wx8_LeqYPnqSKGQKDhrstg

标签:Reactor,模型,高性能,线程,事件,IO,多线程
来源: https://www.cnblogs.com/zhongqifeng/p/16029629.html