在 Netty 中做耗时的,不可预料的操作,比如数据库,网络请求,会严重影响 Netty 对 Socket 的处理速度。
而解决方法就是将耗时任务添加到异步线程池中。但就添加线程池这步操作来讲,可以有 2 种方式,而且这 2种方式实现的区别也蛮大的

方式一:在handler中加入线程池
  1. static final EventExecutorGroup GROUP = new DefaultEventExecutorGroup(16);
  2. @Override
  3. public void channelRead(ChannelHandlerContext ctx, Object msg){
  4. GROUP.submit(() -> {
  5. ByteBuf buf = (ByteBuf) msg;
  6. byte[] req = new byte[buf.readableBytes()];
  7. buf.readBytes(req);
  8. String s = new String(req, StandardCharsets.UTF_8);
  9. try {
  10. Thread.sleep(10*1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println(s);
  15. String reqs = "hello";
  16. ByteBuf resp = Unpooled.copiedBuffer(reqs.getBytes(StandardCharsets.UTF_8));
  17. ctx.writeAndFlush(resp);
  18. return null;
  19. });
  20. System.out.println("go");
  21. }

说明:
1.在ChannelRead方法中,模拟了一个耗时10s的操作,这里我们将这个任务提交到一个自定义的业务线程池中,这样就不会阻塞Netty的IO线程
逻辑图如下
image.png
说明:
1.解释上图:当IO线程轮询到一个socket事件,然后,IO线程开始处理,当走到耗时handler的时候,将任务交给业务线程池
2.当耗时任务执行完毕后再执行pipeline write 方法时,会将这个任务交给IO线程


write方法说明

1.当判定下个outbound 的executor线程不是当前线程的时候,会将当前的任务封装成为task,然后放入mpsc队列中,等待IO任务执行完毕后执行队列中的任务


方式二:在context中添加线程池

在pipeline中添加handler的时候,添加一个线程池

  1. static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
  2. ServerBootstrap b = new ServerBootstrap();
  3. b.group(bossGroup, workerGroup)
  4. .channel(NioServerSocketChannel.class)
  5. .option(ChannelOption.SO_BACKLOG, 100)
  6. .handler(new LoggingHandler(LogLevel.INFO))
  7. .childHandler(new ChannelInitializer<SocketChannel>() {
  8. @Override
  9. public void initChannel(SocketChannel ch) throws Exception {
  10. ChannelPipeline p = ch.pipeline();
  11. if (sslCtx != null) {
  12. p.addLast(sslCtx.newHandler(ch.alloc()));
  13. }
  14. p.addLast(group, new EchoServerHandler());
  15. }
  16. });

说明:
1.handler 中的代码就是使用普通的方式来处理耗时任务
2.当我们调用addLast方法添加线程池后,handler将优先使用这个线程池,如果不添加,将使用IO线程


两种方式的比较

1.第一种方式在handler种添加异步,可能更加自由,比如如果需要访问数据库等操作,就进行异步,如果不需要,就不异步,异步会拖长接口响应时间。因为需要将任务放进mpscTask种,如果IO时间很短,,task很多,可能一个循环下来,都没时间执行整个task,导致响应时间不达标
2.第二种方式是Netty标准方式(即加入到队列),但是,这么做会将整个handler都交给业务线程池,不论是否耗时,都会加入到队列中,不够灵活