Netty 线程模型
1. 前言
前面几节分别讲解了 Reactor 的三种线程模型,都知道主从 Reactor 多线程模型的性能非常的好,那么 Netty 是否就是使用 主从 Reactor 多线程模型 呢?其实 Netty 线程模型是基于 主从 Reactor 多线程模型 做了一定的改造,Netty 的线程模型要比 Reactor 主从多线程模型还要复杂。本节主要是通过图解的方式逐步分析 Netty 线程模型的原理。
2. Netty 模型介绍
2.1 模型介绍
Netty 模型架构说明:
- Netty 抽象出两个线程池,分别是 BossGroup 和 WorkerGroup,BossGroup 专门负责接受客户端的连接,Worker 请求处理;
- BossGroup 和 WorkerGroup 类型默认使用的是 NioEventLoopGroup;
- NioEventLoopGroup 是一个定时任务线程池,NioEventLoop 是真正工作的线程;
- 每个 BossGroup 的 NioEventLoop 分别循环执行三个步骤
4.1 每个 NioEventLoop 都有一个 Selector,并且不断轮询 accept 事件;
4.2 处理 accept 事件,与客户端建立连接,生成 NioSocketChannel,并且将其注册到某个 WorkerGroup 下的 NioEventLoop 上的 Selector 上;
4.3 处理任务队列中的任务,即 runAllTasks。 - 每个 WorkerGroup 的 NioEventLoop 分别循环执行三个步骤
5.1 轮询 read 和 write 事件;
5.2 处理 I/O 事件,即 read,write 事件,并在其对应的 NioSocketChannel 处理;
5.3 处理任务队列的任务,即 runAllTasks。
2.2 核心概念理解
Tips: 额外知识点补充,这里提前剧透一下 Channel、ChannelPipeline、ChannelHanlder 之间的关系
- 每个客户端连接进来的时候,服务端都会建立一个 Channel;
- 为每个 Channel 绑定一个 NioEventLoop 线程,该线程主要负责处理该 Channel 的业务,一个 Channel 对应一个 NioEventLoop,但是一个 NioEventLoop 可以同时服务多个 Channel;
- 为每个 Channel 绑定一个 ChannelPipeline,它是一个业务管道,专门负责管理业务链,也就是 ChannelHandler;
- WorkerGroup 的核心方法是 runAllTasks (),它主要是触发 NioEventLoop 去处理对应的 Channel 里面的 ChannelPipeline 里面的 ChannelHandler 里面的业务逻辑。
3. Netty 模型配置
3.1 单线程配置
在 ServerBootstrap 调用方法 group 的时候,传递的参数是同一个线程组,且在构造线程组的时候,构造参数为 1。
实例:
public class ServerNetty{
private ServerBootstrap bootstrap=null;
private EventLoopGroup group=null;
public void init(){
group=new NioEventLoopGroup(1);//线程数量为 1
bootstrap.group(group,group);
}
}
3.2 多线程配置
在 ServerBootstrap 调用方法 group 的时候,传递的参数是两个不同的线程组,负责监听的 acceptor 线程组的线程数为 1,负责处理客户端线程组的线程数大于 1。
实例:
public class ServerNetty{
private ServerBootstrap bootstrap=null;
private EventLoopGroup acceptorGroup=null;
private EventLoopGroup clientGroup=null;
public void init(){
acceptorGroup=new NioEventLoopGroup(1);//线程数量为 1
clientGroup=new NioEventLoopGroup();//默认是 cpu 的核心数
bootstrap.group(acceptorGroup,clientGroup);
}
}
3.3 主从多线程配置
在 ServerBootstrap 调用方法 group 的时候,传递的参数是两个不同的线程组,负责监听的 acceptor 线程组的线程数大于 1,负责处理客户端线程组的线程数大于 1。
实例:
public class ServerNetty{
private ServerBootstrap bootstrap=null;
private EventLoopGroup acceptorGroup=null;
private EventLoopGroup clientGroup=null;
public void init(){
acceptorGroup=new NioEventLoopGroup();//默认是 cpu 的核心数
clientGroup=new NioEventLoopGroup();//默认是 cpu 的核心数
bootstrap.group(acceptorGroup,clientGroup);
}
}
4. 自定义任务队列
通常情况下,任务队列中常见的任务主要有以下几种类型:
- 用户自定义的异步任务,比如:依赖线程池去异步某个任务等;
- 用户自定义的定时任务,比如:依赖定时线程池去定义每隔 n 秒执行某个任务等;
- 非当前 reactor 线程调用 channel 的各种方法。
4.1 异步任务
其实跟我们平时使用线程池没有什么区别,只不过调用的是底层 Netty 线程组。
实例:
//使用 reactor 线程的异步任务
ctx.channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
//...
}
});
//使用线程池去实现异步任务
ExecutorService es = Executors.newFixedThreadPool(5);
es.execute(new Runnable() {
@Override
public void run() {
}
});
4.2 定时任务
其实类似我们平时使用的定时任务线程池(如:ScheduledThreadPool),只不过是调用底层 Netty 线程组。
实例:
//使用 reactor 线程实现的定时任务
ctx.channel().eventLoop().schedule(new Runnable() {
@Override
public void run() {
}
}, 60, TimeUnit.SECONDS);
//使用线程池去实现定时任务
ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);
ses.schedule(new Runnable() {
public void run() {
System.out.println("i:" + temp);
}
}, 3, TimeUnit.SECONDS);
总结:
- 当前 reactor 线程调用当前 eventLoop 执行任务,直接执行,否则,添加到任务队列稍后执行;
- netty 内部的任务分为普通任务和定时任务,分别落地到 MpscQueue 和 PriorityQueue;
- netty 每次执行任务循环之前,会将已经到期的定时任务从 PriorityQueue 转移到 MpscQueue;
- netty 每隔 64 个任务检查一下是否该退出任务循环。
5. 小结
本节主要掌握的核心知识点
- Netty 的模型的理解,以及每个 NioEventLoop 所执行的三个核心操作,分别是①轮询出 IO 事件;②处理 IO 事件;③处理任务队列;
- 了解 Channel、ChannelPipeline、ChannelHandler 之间的关系,以及 NioEventLoop 主要负责处理每个 Channel 的业务逻辑;
- Netty 如何配置三种 Reactor 模型;
- 如何使用内置的 NioEventLoop 执行自定义的异步任务和定时任务。
本文来自互联网用户投稿,不拥有所有权,该文观点仅代表作者本人,不代表本站立场。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,邮箱:80764001@qq.com,予以删除。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,邮箱:80764001@qq.com,予以删除。