详解netty 线程模型及EventLoop
一、线程模型
linux下网络并发线程模型主要有下面五种
1、apache模型:
为每一种连接分配一个进程。主机分配给每个连接的时间和空间代价大,并且随着连接的增多,进程间切换开销也增长了,很难应对大量的客户并发连接。
2、TPC模型(Thread per connection):
每个线程对应一个连接。比apache模型好,但是还是存在性能开销大的问题。
3、select模型:I/O多路复用
1、每个连接对应一个文件描述符。select模型受限域FD_SETSIZE
即进程最大打开的描述符数。一般为1024个
2、select 每次都会扫描一个文件描述符的集合,集合越大扫描的效率越低。
3、内核到用户空间,会发生文件的拷贝
4、poll模型:I/O多路复用
1、poll模型解决了最大文件描述符的问题,但是依然存在扫描效率和内存拷贝的问题
5、epoll模型:I/O多路复用
1、无文件描述符大小的限制仅与内存大小相关
2、epoll返回时已经明确直到时哪个socket 已经有准备好的读写事件,避免了像select模型那样的扫描问题
3、内核到用户空间采用共享内存方式,实现了零拷贝
二、netty的线程模型
netty可以支持单线程、多线程、主从多线程模型。
- reactor单线程模型的示意图如下:
单个线程一非阻塞IO的模式处理所有的IO事件,包括连接、读写、关闭等。
- reactor多线程模型的示意图如下:
一个线程接收连接,一组线程处理IO读写操作事件。
- reactor主从线程模型的示意图如下:
三、 EventLoop
运行任务来处理在连接的生命周期内发生的事件是任何网络框架的基本功能。与其对应的编程术语被称为事件循环----netty中使用了io.netty.channel.EventLoop
。
查看EventLoop接口的定义及注释信息
/**
* Will handle all the I/O operations for a {@link Channel} once registered.
*
* One {@link EventLoop} instance will usually handle more than one {@link Channel} but this may depend on
* implementation details and internals.
*
*/
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}
可以看出,一个channel一旦注册后,就会使用同一个EventLoop来完成其所有的I/o操作。而每一个EventLoop将由一个永远不会改变的Thread来驱动。所以使用netty编程,线程是安全的,可以避免很多的同步操作。
Netty的EventLoop是协同设计的一部分,它包含两个基本的API:并发和网络编程。首先 io.netty.util.concurrent
包构建在JDK的java.util.concurrent包上,用来提供线程执行器。其次,io.netty.channel
包中类,是为了与Channel的事件进行交互,扩展了这些接口/类。
NioEventLoop的类的层次结构如下:
1、线程管理
Netty线程模型的卓越性能取对于当前执行Thread的身份确定。确定当前线程是否Channel绑定的那个线程。如果调用线程正是支撑EventLoop的线程,那么就提交的代码块将会直接执行。否则,EventLoop将调度该任务以便稍后执行,并将它放入到内部队列中。
2、线程分配
服务于channel的I/o事件的EventLoop包含在EventLoopGroup中。根据不同的传输实现,EventLoop的创建和分配方式不同。
1)、异步传输
异步传输只使用了少量的EventLoop,通过尽可能少的Thread来支撑大量的Channel,而不是为每个Channel分配一个Thread。如下图所示,一个具有3个固定大小EventLoop。在创建EventLoopGroup时就直接分配了EventLoop,以确保在需要时他们时可用的。
2)、阻塞传输
阻塞传输的时候会为每一个Channel分配一个EventLoop。如下图所示: