EventLoop
EventLoop
1、事件循环对象
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}
2、本质:一个单线程执行器
(1)同时维护一个 Selector
(2)其中 run 方法处理 Channel 上的 IO 事件
3、Javadoc 描述:一旦注册,将处理一个通道的所有 I/O 操作,一个 EventLoop 实例通常会处理一个以上的通道,但这可能取决于实现细节和内部结构
4、继承自 JUC 的 ScheduledExecutorService,包含其线程池中所有的方法
5、继承自 Netty 的 OrderedEventExecutor
(1)提供 boolean inEventLoop(Thread thread) 方法,判断一个线程是否属于此 EventLoop
(2)提供 parent 方法,查看自身属于哪个 EventLoopGroup
6、一般不直接创建 EventLoop,而是使用 EventLoopGroup
EventLoopGroup
1、事件循环组
public interface EventLoopGroup extends EventExecutorGroup
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor>
2、一组 EventLoop,可以理解为:线程池 + Selector
(1)Channel 一般会调用 EventLoopGroup 的 register 方法,绑定其中一个 EventLoop,该 Channel 上的 IO 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
(2)next 方法,获取集合中下一个 EventLoop
3、继承关系
(1)继承 Netty 的 EventExecutorGroup
(2)实现 Iterable 接口,可以遍历 EventLoop
4、Javadoc 描述:特殊的 EventExecutorGroup,它允许注册 Channel,Channel 在 EventLoop 中被处理
EventLoopGroup 常用实现
1、NioEventLoopGroup
(1)功能全面:处理 IO 事件,提交普通任务、定时任务
(2)继承关系
public class NioEventLoopGroup extends MultithreadEventLoopGroup
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup
(3)空构造器
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup(
int nThreads, ThreadFactory threadFactory, final SelectorProvider selectorProvider) {
this(nThreads, threadFactory, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
//若线程数为0,采用默认值;若不为0,则以指定值为准
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
//所获取的系统参数,与 1 比较,取最大值
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
//读取系统参数:io.netty.eventLoopThreads,若成功读取,则以其为准
//若没有指定系统参数,则为:系统核心数 * 2
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
2、DefaultEventLoopGroup
(1)提交普通任务、定时任务
(2)继承关系
public class DefaultEventLoopGroup extends MultithreadEventLoopGroup
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup
优雅关闭 EventLoopGroup
1、shutdownGracefully 方法:该方法会首先切换 EventLoopGroup 到关闭状态,从而拒绝新的任务的加入,然后在任务队列的任务都处理完成后,停止线程的运行,从而确保整体应用是在正常有序的状态下退出的
2、AbstractEventExecutor 抽象类
(1)EventExecutorGroup.shutdownGracely(long, long, TimeUnit) 的快捷方式方法,具有合理的默认值
@Override
public Future<?> shutdownGracefully() {
return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
}
static final long DEFAULT_SHUTDOWN_QUIET_PERIOD = 2;
static final long DEFAULT_SHUTDOWN_TIMEOUT = 15;
3、AbstractEventExecutor 接口
(1)shutdownGracefully(long, long, TimeUnit) 的便捷方法,具有合理的默认值
Future<?> shutdownGracefully()
(2)向该执行器发出信号,表明调用者希望该执行器被关闭。一旦这个方法被调用,isShuttingDown() 就开始返回 ,并且执行器准备关闭自己。与 shutdown() 不同,graceful shutdown 确保在它关闭自己之前的“安静期”(通常是几秒钟)没有任务提交。如果在安静期提交了一个任务,它保证会被接受,并且安静期将重新开始。
//quietPeriod - 文档中描述的安静期
//timeout - 等待执行器shutdown()的最大时间,无论是否有任务在安静期间提交
//unit - quietPeriod和timeout的单位
Future<?> shutdownGracefully(long quietPeriod,
long timeout,
TimeUnit unit)
EventLoop、Channel 关系
1、一个 EventLoop 可以负责多个 Channel
2、EventLoop 一旦与 Channel 绑定,则一直负责处理该 Channel 中的事件
细分服务端 EventLoopGroup 职责
1、AbstractBootstrap 子类,允许轻松引导 ServerChannel
public class ServerBootstrap
extends AbstractBootstrap<ServerBootstrap,ServerChannel>
2、为父(接受器)和子(客户端)设置事件循环组,这些 EventLoopGroup 用于处理 ServerChannel 和 Channel 的所有事件和 IO
public ServerBootstrap group(EventLoopGroup parentGroup,
EventLoopGroup childGroup)
(1)parentGroup:只负责 ServerSocketChannel,因为只有一个 ServerSocketChannel,所以不需要指定线程数(Selector 数),使用空构造器即可
(2)childGroup:只负责 SocketChannel
细分只负责 SocketChannel 的 EventLoopGroup 职责
1、原因:多路复用通过轮询方式检测是否有事件发生,并且对到达的事件逐一进行响应,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询
2、实现
(1)当有的任务需要较长的时间处理时,可以使用非 NioEventLoopGroup,避免同一个 NioEventLoop 中的其他 Channel 在较长的时间内都无法得到处理
(2)创建独立 DefaultEventLoopGroup / NioEventLoopGroup
(3)获取 ChannelPipeline 后,使用 addAfter / addBefore / addFirst / addLast,添加 ChannelHandler、EventLoopGroup,两者会自动绑定
3、ChannelInboundHandlerAdapter 类
(1)ChannelInboundHandler 实现的抽象基类,提供其所有方法的实现
(2)这个实现只是把操作转发给 ChannelPipeline 中的下一个 ChannelHandler,子类可以重写一个方法的实现来改变这一点
4、ChannelPipeline 接口
(1)多个 ChannelHandler 的一个列表,它处理或拦截一个 Channel 的入站事件和出站操作
(2)ChannelPipeline 实现拦截过滤器模式的高级形式,让用户完全控制事件的处理方式,以及管道中的多个 ChannelHandler 如何相互作用
(3)addAfter、addBefore、addFirst、addLast,其中 EventExecutorGroup group 参数,将用于执行 ChannelHandler handler 的方法
不同 EventLoopGroup 的 ChannelHandler 之间切换
1、ChannelHandlerContext 接口
(1)使得 ChannelHandler 能够与它的 ChannelPipeline 和其他 handler 交互
(2)在其他方面,handler 可以通知 ChannelPipeline 中的下一个 ChannelHandler,以及动态地修改它所属的 ChannelPipeline
(3)基本实现类:AbstractChannelHandlerContext
2、以 fireChannelRead 为例
(1)一个通道收到一条消息,这将导致 ChannelPipeline 中包含的下一个 ChannelInboundHandler.channelRead(ChannelHandlerContext, Object) 方法被调用
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
(2)如果两个 handler 绑定的是同一线程(EventLoop),则直接调用
(3)如果两个 handler 绑定的是不同线程(EventLoop),则把要调用的代码封装为一个 Runnable 对象,由下一个 handler 的 EventLoop 来调用
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
//获得下一个 handler 的 EventLoop,excutor 即为 EventLoopGro
EventExecutor executor = next.executor();
//判断下一个 handler 的 EventLoop 是否与当前 handler 的 EventLoop 为同一线程(EventLoop)
if (executor.inEventLoop()) {
//若为同一线程(EventLoop),则使用当前 EventLoopGroup 中的 EventLoop 来处理任务
next.invokeChannelRead(m);
} else {
//否则,传入 Runnable 对象,让下一个 handler 的 EventLoop 来创建任务并执行
executor.execute(new Runnable() {
public void run() {
next.invokeChannelRead(m);
}
});
}
}
3、EventExecutor 的 inEventLoop 方法
(1)判断一个线程是否为 EventExecutor 内部线程
(2)EventExecutor、EventLoop 都是单线程实现
(3)当 I/O 变化时,通过 channel 关联的 pipeline 会触发对应的事件,这些事件对应执行 pipeline 的处理链中 handler 的回调方法,每个 handler 添加到 pipeline 都可以指定自己的 EventLoop
(4)如果没指定,默认使用要添加的 pipeline 关联的 channel 注册到的 EventLoopGroup 中的某个 EventLoop
(5)所以 channel 通过 pipeline 调用 handler 时,如果 handler 没有单独指定 EventLoop,则 inEventLoop 就会返回 true,由同一个线程处理,直接调用 handler
(6)如果 handler 单独指定 EventLoop,则 inEventLoop 就会返回 false,调用 handler 时,就把所要调用的方法封装到 Runnable,然后添加到 handler 指定 EventLoop 的任务队列里,稍后会由对应 EventLoop 中的线程执行
Javadoc 描述
1、EventExecutor
(1)EventExecutor 是一个特殊的 EventExecutorGroup,它带有一些方便的方法来查看一个 Thread,是否在一个事件循环中被执行
(2)除此之外,它还扩展了EventExecutorGroup,允许以一种通用的方式来访问方法
2、EventExecutorGroup
(1)EventExecutorGroup 负责通过其 next() 方法,提供要使用的 EventExecutor
(2)除此之外,它还负责处理它们的生命周期,并允许以全局的方式关闭它们
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战