为什么需要自定义线程处理
在Netty中,EventLoop 和 EventExecutorGroup 提供的普通任务不能解决长耗时业务处理引起的 I/O 线程阻塞问题,究其原因:在 Netty 中,一个 channel 在它的生命中期内只注册于一个 EventLoop,而一个 EventLoop 在它的生命周期内只和一个线程绑定,一个给定的 channel 的 I/O 操作都是由相同的线程进行处理。为此不能直接在该线程上进行长耗时业务处理。为了解决这个问题,通常的做法是:自定义线程池,把长耗时的业务都丢到线程池中去处理。
Guava 自定义线程池处理业务
基本实现思路如下:
- 引入 Guava 依赖,Guava是谷歌推出的基于开源的 Java 库,是谷歌很多项目的核心库,该库是为了增强 Java
的功能和处理能力,我们利用它来实现我们的业务线程池; - 在 Handler 中定义业务线程池,我们利用 Guava 创建线程池,这里面包含了线程启动的方式,线程池必要参数的设置,注意这里一定要设置线程为守护线程而不是用户线程,要不然麻烦多多,守护线程和用户线程的主要区别:主线程结束后用户线程还会继续运行,守护线程则会自动结束;
- 耗时业务放入线程池:用法相当简单:service.submit (我们要执行的业务处理),和我们前面讲到的普通任务和定时任务的放置完全一样。
添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
编码实现
服务端实现
package org.skystep.tcpserv;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup wokerGroup = new NioEventLoopGroup();
try {
//用于启动NIO服务
ServerBootstrap serverBootstrap = new ServerBootstrap();
//通过工厂方法设计模式实例化一个channel
serverBootstrap.group(bossGroup, wokerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ServerInitializer());
//绑定服务器,该实例将提供有关IO操作的结果或状态的信息
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
System.out.println("在" + channelFuture.channel().localAddress(