netty版本
- 使用的netty版本是
io.netty:netty-all:4.1.33.Final
Netty耗时任务的处理
- Netty中
EventLoop
用来处理IO线程,因此handler
中的耗时任务(比如数据库连接、远程调用等)不能在EventLoop
里面执行。如果有耗时任务,需要将耗时任务添加到业务线程池中执行。 - 处理耗时任务的两种方式
Handler
中加入线程池Context
中添加线程池
- 两种方式的对比
Handler
中自定义业务线程池更加灵活,每个Handler
都可以自己控制自己的业务线程池,对于非耗时的任务可以不使用业务线程池,直接在EventLoop
线程中执行。Context
中添加线程池(Netty建议的方式),在pipeline
中添加Handler
的时候,添加一个业务线程池。这种方式Handler
中的代码不需要做任何修改,但是整个Handler
都交给业务线程池,无论是否是耗时任务都会加入到队列里,控制的粒度比较粗。
- 如果业务
ChannelHandler
处理逻辑比较简单,执行时间是受控的,业务I/O线程的负载也不重,在这种应用场景下,业务ChannelHandler
可以和I/O操作共享同一个线程。使用这种线程模型会带来两个优势:- 开发简单,开发业务
ChannelHandler
的不需要关注Netty的线程模型 - 性能更高:因为减少了一次线程上下文切换,所以性能会更高。
- 开发简单,开发业务
- Netty线程池的特点
Channel
和EventLoop
是N:1
的关系:一个Channel
生命周期内只注册一个EventLoop
,一个EventLoop
可能会给分配给多个Channel
,所以如果一个Channel
阻塞可能会导致其他在同一个EventLoop
上的Channel
都阻塞。- 一个
EventLoopGroup
包含一个或者多个EventLoop
,一个EventLoop
在其生命周期内只与一个Thread
绑定(EbeddedEventLoop
除外)EventLoop
的Thread
负责处理该事件循环内所有的IO事件
代码案例
Handler
自定义业务线程池
-
Handler
中自定义业务线程池@ChannelHandler.Sharable public class EchoServerHandler extends SimpleChannelInboundHandler<String> { public static final ChannelHandler INSTANCE = new EchoServerHandler(); private static final String LINE = System.getProperty("line.separator"); private EchoServerHandler() { } protected static ExecutorService newFixedThreadPool() { final ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat("netty-business-%d") .setDaemon(false) .build(); return new ThreadPoolExecutor(200, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10000), threadFactory); } final static ListeningExecutorService service = MoreExecutors.listeningDecorator(newFixedThreadPool()); @Override public void channelRead0(ChannelHandlerContext ctx, String msg) { service.submit(new Runnable() { @Override public void run() { //模拟耗时任务 try