在 Netty 中做耗时的,不可预料的操作,比如数据库,网络请求,会严重影响 Netty 对 Socket 的处理速度。
而解决方法就是将耗时任务添加到异步线程池中。但就添加线程池这步操作来讲,可以有 2 种方式,而且这 2种方式实现的区别也蛮大的
方式一:在handler中加入线程池
static final EventExecutorGroup GROUP = new DefaultEventExecutorGroup(16);@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg){GROUP.submit(() -> {ByteBuf buf = (ByteBuf) msg;byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);String s = new String(req, StandardCharsets.UTF_8);try {Thread.sleep(10*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(s);String reqs = "hello";ByteBuf resp = Unpooled.copiedBuffer(reqs.getBytes(StandardCharsets.UTF_8));ctx.writeAndFlush(resp);return null;});System.out.println("go");}
说明:
1.在ChannelRead方法中,模拟了一个耗时10s的操作,这里我们将这个任务提交到一个自定义的业务线程池中,这样就不会阻塞Netty的IO线程
逻辑图如下
说明:
1.解释上图:当IO线程轮询到一个socket事件,然后,IO线程开始处理,当走到耗时handler的时候,将任务交给业务线程池
2.当耗时任务执行完毕后再执行pipeline write 方法时,会将这个任务交给IO线程
write方法说明
1.当判定下个outbound 的executor线程不是当前线程的时候,会将当前的任务封装成为task,然后放入mpsc队列中,等待IO任务执行完毕后执行队列中的任务
方式二:在context中添加线程池
在pipeline中添加handler的时候,添加一个线程池
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();if (sslCtx != null) {p.addLast(sslCtx.newHandler(ch.alloc()));}p.addLast(group, new EchoServerHandler());}});
说明:
1.handler 中的代码就是使用普通的方式来处理耗时任务
2.当我们调用addLast方法添加线程池后,handler将优先使用这个线程池,如果不添加,将使用IO线程
两种方式的比较
1.第一种方式在handler种添加异步,可能更加自由,比如如果需要访问数据库等操作,就进行异步,如果不需要,就不异步,异步会拖长接口响应时间。因为需要将任务放进mpscTask种,如果IO时间很短,,task很多,可能一个循环下来,都没时间执行整个task,导致响应时间不达标
2.第二种方式是Netty标准方式(即加入到队列),但是,这么做会将整个handler都交给业务线程池,不论是否耗时,都会加入到队列中,不够灵活
