Netty 异步线程池处理任务
2020-07-05 本文已影响0人
马路边的小破孩
前提
在Netty中做耗时的,不可预料的操作,比如数据库的操作,网络请求等,会严重影响Netty对Socket的处理速度。
解决方式
解决方式就是将耗时任务添加到异步线程池当中。就添加线程池这部操作来说,有2种方式:
- handler中加入线程池
- Context中添加线程池
下面我们就来分析这两种方式。
1. handler中加入线程池
Server端代码:
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap().group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new StringDecoder())
.addLast(new TaskHandler())
;
}
});
try {
ChannelFuture channelFuture = bootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
TaskHandler 代码:
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//在handler中处理耗时任务
System.out.println("当前handler处理线程名称:"+Thread.currentThread().getName());
ctx.channel().eventLoop().submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000L);
System.out.println("当前异步任务线程名称:"+Thread.currentThread().getName());
ctx.channel().writeAndFlush(Unpooled.copiedBuffer("hello~客户端".getBytes()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
super.channelActive(ctx);
}
运行打印输出结果:
当前handler处理线程名称:nioEventLoopGroup-3-2
当前异步任务线程名称:nioEventLoopGroup-3-2
2. Context中添加线程池
Netty为我们提供了一个线程池EventExecutorGroup的接口及其很多实现类,下面我们使用DefaultEventExecutorGroup 线程池异步处理任务,来对比一下 handler中异步线程执行任务的区别。
不加入DefaultEventExecutorGroup
Server端代码:
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap().group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new StringDecoder())
.addLast(new TaskHandler())
;
}
});
try {
ChannelFuture channelFuture = bootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
TaskHandler代码:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//在handler中处理耗时任务
System.out.println("当前handler处理线程名称:"+Thread.currentThread().getName());
super.channelActive(ctx);
}
运行输出执行结果(运行三个客户端):
当前handler处理线程名称:nioEventLoopGroup-4-1
当前handler处理线程名称:nioEventLoopGroup-4-2
当前handler处理线程名称:nioEventLoopGroup-4-3
加入DefaultEventExecutorGroup
Server端代码:
public static void main(String[] args) {
DefaultEventExecutorGroup eventExecutors = new DefaultEventExecutorGroup(4);
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap().group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new StringDecoder())
//如果在handler前有添加EventExecutorGroup
//则该handler会优先添加到该线程池中
.addLast(eventExecutors,new TaskHandler())
;
}
});
try {
ChannelFuture channelFuture = bootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
Client端代码不变,和上面保持一致。
输出打印结果(运行三个客户端):
当前handler处理线程名称:defaultEventExecutorGroup-2-1
当前handler处理线程名称:defaultEventExecutorGroup-2-2
当前handler处理线程名称:defaultEventExecutorGroup-2-3
对比我们可以看出来,如果在handler前有添加DefaultEventExecutorGroup , 则该handler会优先添加到该线程池中,处理任务的线程池不在是NioEventLoopGroup。
3.两种方式比较
1.第一种方式在handler中添加异步,可能更加的自由,比如如果需要访问数据库,那我就异步,如果不需要就不异步,异步会拖长接口响应时间。因为需要将任务放进task中,如果IO时间很短,task很多,可能一个循环下来,都没时间执行整个task,导致响应时间不达标。
- 第二中方式是Netty标准方式即加入到队列,但是这么做会将整个handler都交给业务线程池,不论耗时不耗时都加入队列,不够灵活。
3.各有优劣,灵活性来说,第一种好。