netty作为一个高性能io框架,可以同时处理上千个并发客户端连接和请求,底层通过selector/epoll等io多路复用实现非阻塞Io,上层使用比较常用reactor io线程模型来保证服务端在大并发场景能够稳定运行.
一 Reactor三种模型
常见的三种reactor三种线程模型:
- reactor 单线程模型
- reactor 多线程模型
- 主从reactor 多线程模型
reactor 单线程模型
单线程模型处理client连接和连接请求都在一个线程内,这种模型适合业务处理比较快的场景对并发处理没有太高要求的场景
netty创建单线程:
EventLoopGroup reactorGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(reactorGroup, reactorGroup)
......
reactor 多线程模型
多线程与单线程区别在于,多了一组线程池用于处理网络IO的读写操作,专门有一个线程负责处理客户端的连接请求.
在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极特殊应用场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务端需要对客户端的握手信息进行安全认证,认证本身非常损耗性能。这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型--主从Reactor多线程模型。
netty实例:
EventLoopGroup acceptorGroup = new NioEventLoopGroup(1);
NioEventLoopGroup ioGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(acceptorGroup, ioGroup)
......
主从reactor 多线程模型
特点是:服务端用于接收客户端连接的不再是1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到I/O线程池(sub reactor线程池)的某个I/O线程上,由它负责SocketChannel的读写和编解码工作。
Acceptor线程池只用于客户端的登录、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的I/O线程上,有I/O线程负责后续的I/O操作。
第三种模型比起第二种模型,是将Reactor分成两部分,mainReactor负责监听server socket,accept新连接,并将建立的socket分派给subReactor。subReactor负责多路分离已连接的socket,读写网 络数据,对业务处理功能,其扔给worker线程池完成。通常,subReactor个数上可与CPU个数等同。
二 EventLoop
netty io的主要处理流程
EventLoop就如起的名字一样,负责一整个io的循环,如上图EventLoop会一直获取selector里面已经ready的keys然后处理这些key,接着运行存在于TaskQueue里面的task.而且EventLoop继承自EventExecutor(继承ScheduledExecutorService),所以我们可以直接提交一个任务到EventLoop.
直接提交task到EventLoop的优势在于我们不必担心多线程同步的问题,EventLoop会保证所有任务都在一个线程内完成,但是如果是比较耗时的任务会阻塞住io,直接影响netty的性能.所以如果是耗时比较大的任务还是单开一个线程比较靠谱.
EventLoop处理io的流程,如下图
EventLoop收到IO事件后会判断是否是本地线程如果是直接执行handle,如果不是会提交任务队列里面,等待EventLoop ready后执行
三 EventLoop和EventLoopGroup关系
EventLoop和EventLoopGroup更像父子关系,EventLoop由EventLoopGroup产生,并且由EventLoopGroup持有线程池并分配指定线程给EventLoop.
netty内部线程分配细节:
threadpool由EventLoopGroup持有,每一个EventLoop对应一个线程,每一个EventLoop可以分配多个channel,但是一个channel只能对应一个线程.
每当一个新的连接建立,由EventLoopGroup分配这个channel(连接)到哪个EventLoop,EventLoopGroup采用round-
robin来确定分配到哪个EventLoop,所以这种分配不是100%均匀,但是大部分时候是均匀的.