【Netty】EventLoop和线程模型
一、前言
在学习了ChannelHandler和ChannelPipeline的有关细节后,接着学习Netty的EventLoop和线程模型。
二、EventLoop和线程模型
2.1. 线程模型
线程池可通过缓存和复用已有线程来提高系统性能,基本的缓冲池模式可描述如下:
· 从池中空闲链表中选取线程,然后将其分配赋予给已提交的任务。
· 当线程完成工作时,该线程又返回至空闲链表,可再进行复用。
该模式如下图所示。

池化和复用线程是针对每个任务都需要创建和销毁线程的改进,但还是需要进行上下文切换,并且随着线程数量的增加,其负担也会增加。同时,在高并发下也会出现很多线程问题。
2.2. EventLoop接口
任何网络框架的基本功能都是运行任务来处理在连接声明周期中所发生的事件,相应的编程结构通常被称为事件循环。事件循环的基本思想如下代码所示,每个任务都是一个Runnable实例。
while (!terminated) {
List<Runnable> readyEvents = blockUntilEventsReady();
for (Runnable ev: readyEvents) {
ev.run();
}
}
Netty的EventLoop是使用concurrency和networking两个基本API的协作设计的一部分,Netty中的io.netty.util.concurrent 包基于JDK的java.util.concurrent包进行设计。另外,io.netty.channel包中的类也继承它们,以便与其事件相关联,具体继承关系如下图所示。

在这个模型中,EventLoop由一个永不改变的线程驱动,任务(Runnable或Callable)可以直接提交给EventLoop的实现,以便立即执行或有计划地执行。根据配置和可用内核,可以创建多个EventLoops以优化资源使用,并且可以为单个EventLoop分配服务多个通道。
事件和任务以FIFO的方式被执行,这通过保证以正确的顺序处理字节内容来消除数据损坏的可能性。
1. Netty 4中的I/O和事件处理
由I/O操作触发的事件流过具有一个或多个ChannelHandler的ChannelPipeline时,传播这些事件的方法调用可以由ChannelHandler拦截,并根据需要进行处理,根据事件的不同,需要进行不同的处理,但事件处理逻辑必须具有通用性和灵活性,以处理所有可能的用例,因此,在Netty 4中,所有的I/O操作和事件都由已分配给EventLoop的线程处理。
2. Netty 3中的I/O处理
以前版本中使用的线程模型仅保证入站(上游)事件将在所谓的I/O线程中执行,所有出站(下游)事件由调用线程处理,其需要在ChannelHandlers中仔细同步出站事件,因为不可能保证多个线程不会同时尝试访问出站事件。
2.3 任务调度
有时需要让一个任务稍后(延迟)或定期执行,一个常见的用例是向远程对等体发送心跳消息,以检查连接是否仍然存在。
1. JDK调度API
在Java 5之前,任务调度基于java.util.Timer构建,其使用后台线程,与标准线程具有相同的限制,随后,Java提供了ScheduledExecutorService接口,如下代码在60S后执行任务。
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
ScheduledFuture<?> future = executor.schedule(
new Runnable() {
@Override
public void run() {
System.out.println("60 seconds later");
}
}, 60, TimeUnit.SECONDS); executor.shutdown();
2. 使用EventLoop调度任务
ScheduledExecutorService实现有限制,如为管理池需要创建额外的线程,如果许多任务被调度,这可能会成为系统性能瓶颈。Netty通过使用Channel的EventLoop调度来解决这个问题,如下代码所示。
Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().schedule(
new Runnable() {
@Override
public void run() {
System.out.println("60 seconds later");
}
}, 60, TimeUnit.SECONDS);
60秒后,Runnable实例将由分配给该Channel的EventLoop执行。若想每隔60S执行任务,则需要做如下处理。
Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("Run every 60 seconds");
}
}, 60, 60, TimeUnit.Seconds);
因为EventLoop继承ScheduledExecutorService,因此可以调用ScheduledExecutorService的所有方法。
2.4 实现细节
1. 线程管理
Netty的线程模型的优越性能取决于确定当前正在执行的线程的身份,即是否为分配给当前Channel及其EventLoop的线程。如果调用的是EventLoop的线程,那么直接执行该代码块,否则,EventLoop调度一个任务以供稍后执行,并将其放入内部队列中,当处理下个事件时,会处理队列中的事件,这解释了任何线程为何可以直接与Channel交互,而不需要在ChannelHandler中同步。
每个EventLoop都有自己的任务队列,与其他EventLoop独立,下图显示了EventLoop的执行逻辑。

不要把长时间的任务放在执行队列中,因为它将阻止任何其他任务在同一个线程上执行。如果必须进行阻塞调用或执行长时间运行的任务,建议使用专用的EventExecutor。
2. EventLoop/线程的分配
为通道的I/O和事件提供服务的EventLoops包含在EventLoopGroup,EventLoops创建和分配的方式根据传输实现(异步和阻塞)而有所不同。
· 异步传输。只使用少量的EventLoopGroup,在当前的模型中其在通道中共享。这允许通道由最小数量的线程提供服务,而不是为每个通道分配一个线程。下图展示了包含三个EventLoop(每个EventLoop由一个线程驱动)的EventLoopGroup,EventLoopGroup创建时会直接分配EventLoops(及其线程),以确保它们在需要时可用,EventLoopGroup负责将EventLoop分配给每个新创建的通道,当前的实现是使用循环方法实现均衡分配,相同的EventLoop可被分配给多个通道。

一旦一个Channel被分配了一个EventLoop,它将在其生命周期中一直使用这个EventLoop(和相关联的线程)。同时请注意EventLoop的分配对ThreadLocal影响,因为一个EventLoop通常驱动多个通道,多个通道的ThreadLocal也相同。
· 阻塞传输。OIO的实现与异步传输的实现大不相同,其如下图所示。

每个通道将会分配一个EventLoop(以及相关线程),Channel的IO事件将由独立的线程处理。
三、总结
本篇博文讲解了EventLoop及其线程模型,以及其与通道之间的关系,EventLoopGroup可对应多个EventLoop,一个EventLoop对应一个线程,一个EventLoop可对应多个通道。也谢谢各位园友的观看~
【Netty】EventLoop和线程模型的更多相关文章
- Netty学习摘记 —— 再谈EventLoop 和线程模型
本文参考 本篇文章是对<Netty In Action>一书第七章"EventLoop和线程模型"的学习摘记,主要内容为线程模型的概述.事件循环的概念和实现.任务调度和 ...
- Netty实战七之EventLoop和线程模型
简单地说,线程模型指定了操作系统.编程语言.框架或者应用程序的上下文中的线程管理的关键方面.Netty的线程模型强大但又易用,并且和Netty的一贯宗旨一样,旨在简化你的应用程序代码,同时最大限度地提 ...
- Netty中的EventLoop和线程模型
一.前言 在学习了ChannelHandler和ChannelPipeline的有关细节后,接着学习Netty的EventLoop和线程模型. 二.EventLoop和线程模型 2.1. 线程模型 线 ...
- Netty 框架学习 —— EventLoop 和线程模型
EventLoop 接口 Netty 是基于 Java NIO 的,因此 Channel 也有其生命周期,处理一个连接在其生命周期内发生的事件是所有网络框架的基本功能.通常来说,我们使用一个线程来处理 ...
- Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?
前言 在之前的 SpringBoot 整合长连接心跳机制 一文中认识了 Netty. 但其实只是能用,为什么要用 Netty?它有哪些优势?这些其实都不清楚. 本文就来从历史源头说道说道. 传统 IO ...
- 3 - EventLoop和线程模型-事件循环
a). EventLoopGroup为每个新创建的channel分配一个EventLoop,多个channel对应一个EventLoop. b). 一个EventLoop由一个不变的thread驱动, ...
- Netty源码死磕一(netty线程模型及EventLoop机制)
引言 好久没有写博客了,近期准备把Netty源码啃一遍.在这之前本想直接看源码,但是看到后面发现其实效率不高, 有些概念还是有必要回头再细啃的,特别是其线程模型以及EventLoop的概念. 当然在开 ...
- Reactor三种线程模型与Netty线程模型
文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...
- 从线程模型的角度看Netty的高性能
转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...
随机推荐
- WPF自定义控件(2)——图表设计[1]
0.小叙闲言 除了仪表盘控件比较常用外,还有图表也经常使用,同样网上也有非常强大的图表控件,有收费的(DEVexpress),也有免费的.但我们平时在使用时,只想简单地绘一个图,控件库里面的许多功能我 ...
- 用exe4j将jar包转成.exe文件的教程
标准版教程再这里 http://pan.baidu.com/s/1i3gn0Br 1. 下载安装exe4j文件,并把你的java文件生成jar格式的文件,在桌面新建一个文件夹,把jar文件放进去 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(五)结合MockMvc进行服务端的单元测试
日常啰嗦 承接前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例>,已经讲解了dao层和service层的单元测试,还有控制器这层也不能 ...
- BZOJ2818 与 BZOJ2301【euler,线性筛,莫比乌斯】
题目大意: 给一个范围[1,n],从中找出两个数x,y,使得gcd(x,y)为质数,问有多少对(x,y有序) 解法: 不难,欧拉函数练手题,可以定义集合P ={x|x为素数},那么我们枚举gcd(x, ...
- linux_cmd_list_0
一.文件 touch file # 创建空白文件 rm -rf 目录名 # 不提示删除非空目录(-r:递归删除 -f强制) dos2unix # windows文本转linux文本 unix2dos ...
- javascript原型的意义
prototype属性: 这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面:那些不需要共享的属性和方法,就放在构造函 ...
- Jmeter的逻辑控制器——Controller
逻辑控制器(Logic Controller) --贯穿整个Test Plan中,与各组件执行顺序没关系:目的是用于控制采样器的执行顺序. Simple Controller Simple Contr ...
- 获取camera截屏图片
Camera camera; SpriteRenderer sprRender; Texture2D t2d = New Texture2D(1300, 760, TextureFormat.RGB2 ...
- Linux - 进程间通信 - 匿名管道
一.概念:进程间通信( IPC,InterProcess Communication) 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进城之间要交换数据必须通过内 ...
- html、css、js实现简易计算器
学习HTML,CSS,JS一个月后,想着能自己是否能写出一个简单的东西,故编写了简易的计算器,之前也写过一个坦克大战,坦克大战的有些基本功能没有实现, 故也没有记录下来,想来,对这行初来咋到的,还是需 ...