Netty学习三:线程模型
1 Proactor和Reactor
Proactor和Reactor是两种经典的多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。
I/O多路复用机制都依赖于一个事件分发器,事件分离器把接收到的客户事件分发到不同的事件处理器中,如下图:
1.1 select,poll,epoll
在操作系统级别select,poll,epoll是3个常用的I/O多路复用机制,简单了解一下将有助于我们理解Proactor和Reactor。
1.1.1 select
select的原理如下:
用户程序发起读操作后,将阻塞查询读数据是否可用,直到内核准备好数据后,用户程序才会真正的读取数据。
poll与select的原理相似,用户程序都要阻塞查询事件是否就绪,但poll没有最大文件描述符的限制。
1.1.2 epoll
epoll是select和poll的改进,原理图如下:
epoll使用“事件”的方式通知用户程序数据就绪,并且使用内存拷贝的方式使用户程序直接读取内核准备好的数据,不用再读取数据
1.2 Proactor
Proactor是一个异步I/O的多路复用模型,原理图如下:
- 用户发起IO操作到事件分离器
- 事件分离器通知操作系统进行IO操作
- 操作系统将数据存放到数据缓存区
- 操作系统通知分发器IO完成
- 分离器将事件分发至相应的事件处理器
- 事件处理器直接读取数据缓存区内的数据进行处理
1.3 Reactor
Reactor是一个同步的I/O多路复用模型,它没有Proactor模式那么复杂,原理图如下:
- 用户发起IO操作到事件分离器
- 事件分离器调用相应的处理器处理事件
- 事件处理完成,事件分离器获得控制权,继续相应处理
1.4 Proactor和Reactor的比较
- Reactor模型简单,Proactor复杂
- Reactor是同步处理方式,Proactor是异步处理方式
- Proactor的IO事件依赖操作系统,操作系统须支持异步IO
- 同步与异步是相对于服务端与IO事件来说的,Proactor通过操作系统异步来完成IO操作,当IO完成后通知事件分离器,而Reactor需要自己完成IO操作
2 Reactor多线程模型
前面已经简单介绍了Proactor和Reactor模型,在实际中Proactor由于需要操作系统的支持,实现的案例不多,有兴趣的可以看一下Boost Asio的实现,我们主要说一下Reactor模型,Netty也是使用Reactor实现的。
但单线程的Reactor模型每一个用户事件都在一个线程中执行:
- 性能有极限,不能处理成百上千的事件
- 当负荷达到一定程度时,性能将会下降
- 单某一个事件处理器发送故障,不能继续处理其他事件
2.1 多线程Reactor
使用线程池的技术来处理I/O操作,原理图如下:
- Acceptor专门用来监听接收客户端的请求
- I/O读写操作由线程池进行负责
- 每个线程可以同时处理几个链路请求,但一个链路请求只能在一个线程中进行处理
2.2 主从多线程Reactor
在多线程Reactor中只有一个Acceptor,如果出现登录、认证等耗性能的操作,这时就会有单点性能问题,因此产生了主从Reactor多线程模型,原理如下:
- Acceptor不再是一个单独的NIO线程,而是一个独立的NIO线程池
- Acceptor处理完后,将事件注册到IO线程池的某个线程上
- IO线程继续完成后续的IO操作
- Acceptor仅仅完成登录、握手和安全认证等操作,IO操作和业务处理依然在后面的从线程中完成
3 Netty中Reactor模型的实现
Netty同时支持Reactor的单线程、多线程和主从多线程模型,在不同的应用中通过启动参数的配置来启动不同的线程模型。
通过线程池的线程个数、是否共享线程池方式来切换不同的模型
3.1 Netty中的Reactor模型
Netty中的Reactor模型如下图:
- Acceptor中的NioEventLoop用于接收TCP连接,初始化参数
- I/O线程池中的NioEventLoop异步读取通信对端的数据报,发送读事件到channel
- 异步发送消息到对端,调用channel的消息发送接口
- 执行系统调用Task
- 执行定时Task
3.2 NioEventLoop
NioEventLoop是Netty的Reactor线程,它在Netty Reactor线程模型中的职责如下:
1. 作为服务端Acceptor线程,负责处理客户端的请求接入
2. 作为客户端Connecor线程,负责注册监听连接操作位,用于判断异步连接结果
3. 作为IO线程,监听网络读操作位,负责从SocketChannel中读取报文
4. 作为IO线程,负责向SocketChannel写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用于后续继续发送半包数据,直到数据全部发送完成
如下图,是一个NioEventLoop的处理链:
- 处理链中的处理方法是串行化执行的
- 一个客户端连接只注册到一个NioEventLoop上,避免了多个IO线程并发操作
3.2.1 Task
Netty Reactor线程模型中有两种Task:系统Task和定时Task
- 系统Task:创建它们的主要原因是,当IO线程和用户线程都在操作同一个资源时,为了防止并发操作时锁的竞争问题,将用户线程封装为一个Task,在IO线程负责执行,实现局部无锁化
- 定时Task:主要用于监控和检查等定时动作
基于以上原因,NioEventLoop不是一个纯粹的IO线程,它还会负责用户线程的调度
3.2.2 IO线程的分配细节
线程池对IO线程进行资源管理,是通过EventLoopGroup实现的。线程池平均分配channel到所有的线程(循环方式实现,不是100%准确),一个线程在同一时间只会处理一个通道的IO操作,这种方式可以确保我们不需要关心同步问题。
3.2.3 Selector
NioEventLoop是Reactor的核心线程,那么它就就必须实现多路复用。
Selector的过程如下:
- 首先oldWakenUp = wakenUp.getAndSet(false)
- 如果队列中有任务, selectNow()
- 如果没有select(),直达channel准备就绪,但此过程中循环次数超过限值也将rebuidSelectoror退出循环
- 执行processSelectedKeys和runAllTasks
epoll-bug的处理
在netty中对java nio的epoll bug进行了处理,就是设置一个阀值,如果超过了就rebuidSelector来避免epoll()死循环
3.2.4 NioEevntLoopGroup
EventExecutorGroup:提供管理EevntLoop的能力,他通过next()来为任务分配执行线程,同时也提供了shutdownGracefully这一优雅下线的接口
EventLoopGroup继承了EventExecutorGroup接口,并新添了3个方法
- EventLoop next()
- ChannelFuture register(Channel channel)
- ChannelFuture register(Channel channel, ChannelPromise promise)
EventLoopGroup的实现中使用next().register(channel)来完成channel的注册,即将channel注册时就绑定了一个EventLoop,然后EvetLoop将channel注册到EventLoop的Selector上。
NioEventLoopGroup还有几点需要注意:
- NioEventLoopGroup下默认的NioEventLoop个数为cpu核数 * 2,因为有很多的io处理
- NioEventLoop和java的single线程池在5里差异变大了,它本身不负责线程的创建销毁,而是由外部传入的线程池管理
- channel和EventLoop是绑定的,即一旦连接被分配到EventLoop,其相关的I/O、编解码、超时处理都在同一个EventLoop中,这样可以确保这些操作都是线程安全的
Netty学习三:线程模型的更多相关文章
- Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?
前言 在之前的 SpringBoot 整合长连接心跳机制 一文中认识了 Netty. 但其实只是能用,为什么要用 Netty?它有哪些优势?这些其实都不清楚. 本文就来从历史源头说道说道. 传统 IO ...
- Netty学习之IO模型
目录 1.1 同步.异步.阻塞.非阻塞 同步 VS 异步 同步 异步 阻塞 VS 非阻塞 阻塞 非阻塞 举例 ...
- Java多线程学习(三)---线程的生命周期
线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...
- Netty学习(三)-Netty重要接口讲解
上一节我们写了一个HelloWorld,对于Netty的运行有了一定的了解,知道Netty是如何启动客户端和服务器端.这一节我们简要的讲解一下几个重要的接口,初步探讨Netty的运行机制,当然刚学Ne ...
- Netty学习(三)高性能之ByteBuf源码解析
原文链接: https://juejin.im/post/5db8ea506fb9a02061399ab3 Netty 的 ByteBuf 类型 Pooled(池化).Unpooled(非池化) Di ...
- Django学习(三) Django模型创建以及操作
在Django中可以建立自己的模型Model,这里对应Java里的实体类,跟数据库表是对应的.其中用到了django.db模块中的models.如下图所示: mysite/news/models.py ...
- Reactor三种线程模型与Netty线程模型
文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...
- Netty In Action中文版 - 第十五章:选择正确的线程模型
http://blog.csdn.net/abc_key/article/details/38419469 本章介绍 线程模型(thread-model) 事件循环(EventLoop) 并发(Con ...
- 从线程模型的角度看Netty的高性能
转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...
随机推荐
- DIY操作系统(一)
先说几句题外话: 回想第一次看到<30天自制操作系统>这本书时,就被这快餐般的标题深深吸引了,我无法想象如此复杂有内涵的内容能在30天就弄出来,直到我花了一个多月看到这本书的第9天时,我放 ...
- Object-C中动态类型对象相关操作汇总
Object-C(以后简称OC)中有id类型,相对于明确定义类型的静态类型,称为动态类型. 使用动态类型,配合多态(不同类型拥有同名方法),动态绑定(运行时决定实际调用的方法)可以将很多判断延迟到运行 ...
- HTML URL 编码
转自:http://www.w3school.com.cn/tags/html_ref_urlencode.htmlURL 编码 - 从 %00 到 %8f ASCII Value URL-encod ...
- selenium 富文本框处理
selenium 富文本框处理, 网上有用API的解决方法1:参见:http://blog.csdn.net/xc5683/article/details/8963621 群里1位群友的解决方法2:参 ...
- android wifi P2P CONNECT, INVITE和JOIN流程选择
android wifi P2P CONNECT, INVITE和JOIN流程选择
- day10---异步I/O,gevent协程
协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来 ...
- [poi2007]mem
题意:给定点数n<=300000的一棵树,然后初始时每条边权值为1,接下来按照时间点先后顺序的n+m-1个操作, 操作有两种: 1.A a b 把a到b的边权改为0 2.W u 求1号点到u号点 ...
- s:textarea中的文本内容在什么时候才能被赋值给Action中的属性?
下面是jsp程序片段: <s:form id="startForm" name ="startForm" action="/hall/hall_ ...
- Windows Server 2008 系统设置集合
1.禁用IPV6 netsh interface teredo set state disabled netsh interface 6to4 set state disabled netsh int ...
- [你必须知道的NOSQL系列]专题一:MongoDB快速入门
一.前言 现在越来越多的公司开始采用非关系数据库了,并且很多公司的面试都要求面试者有MongoDB的使用经验,至于非关系数据库与关系型数据库之间的区别大家可以自行百度.但是作为程序员的我们,既然大部分 ...