netty中的EventLoopGroup 和 EventLoop

当系统在运行过程中,如果频繁的进行线程上下文切换,会带来额外的性能损耗。多线程并发执行某个业务流程,业务开发者还需要时刻对线程安全保持警惕,哪些数据可能会被并发修改,如何保护?这不仅降低了开发效率,也会带来额外的性能损耗。

为了解决上述问题,Netty采用了串行化设计理念,从消息的读取、编码以及后续 ChannelHandler 的执行,始终都由 IO 线程 EventLoop 负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险。 EventLoopGroup 是一组 EventLoop 的抽象,一个 EventLoopGroup 当中会包含一个或多个 EventLoop,EventLoopGroup 提供 next 接口,可以从一组 EventLoop 里面按照一定规则获取其中一个 EventLoop 来处理任务。

在 Netty 服务器端编程中我们需要 BossEventLoopGroup 和WorkerEventLoopGroup 两个 EventLoopGroup 来进行工作。

BossEventLoopGroup 通常是一个单线程的 EventLoop,EventLoop 维护着一个注册了 ServerSocketChannel 的 Selector 实例,EventLoop 的实现涵盖 IO 事件的分离,和分发(Dispatcher),EventLoop 的实现充当 Reactor 模式中的分发(Dispatcher)的角色。

所以通常可以将 BossEventLoopGroup 的线程数参数为 1。

ChannelPipeline 中的每一个 ChannelHandler 都是通过它的 EventLoop(I/O 线程)来处理传递给它的事件的。所以至关重要的是不要阻塞这个线程,因为这会对整体的 I/O 处理产生严重的负面影响。但有时可能需要与那些使用阻塞 API 的遗留代码进行交互。

对于这种情况, ChannelPipeline 有一些接受一个 EventExecutorGroup 的 add() 方法。如果一个事件被传递给一个自定义的 EventExecutorGroup, DefaultEventExecutorGroup 的默认实现。

就是在把 ChannelHanders 添加到 ChannelPipeline 的时候,指定一个 EventExecutorGroup,ChannelHandler 中所有的方法都将会在这个指定的 EventExecutorGroup 中运行。

static final EventExecutor group = new DefaultEventExecutorGroup(16);
...
ChannelPipeline p = ch.pipeline();
pipeline.addLast(group, "handler", new MyChannelHandler());

最后小结一下:

    NioEventLoopGroup 实际上就是个线程池,一个 EventLoopGroup 包含一个或者多个 EventLoop; 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定; 所有有 EnventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理; 一个 Channel 在它的生命周期内只注册于一个 EventLoop; 每一个 EventLoop 负责处理一个或多个 Channel;
经验分享 程序员 微信小程序 职场和发展