Netty优化建议
# 5.1、零拷⻉
Netty的零拷⻉主要体现在三个⽅⾯:
- Bytebuf 使⽤的是⽤池化的Direct Buffer类型使⽤的堆外内存,不需要进⾏字节缓冲区的⼆次拷 ⻉,如果使⽤堆内存,JVM会先拷⻉到堆内,再写⼊Socket,就多了⼀次拷⻉。
- CompositeByteBuf将多个ByteBuf封装成⼀个ByteBuf,在添加ByteBuf时不需要进程拷⻉。
- Netty的⽂件传输类DefaultFileRegion的transferTo⽅法将⽂件发送到⽬标channel中,不需要进 ⾏循环拷⻉,提升了性能。
# 5.2、使⽤EventLoop的任务调度
在EventLoop的⽀持线程外使⽤channel:
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
channel.writeAndFlush(data)
}
});
⽽不是直接使⽤channel.writeAndFlush(data);
前者会直接放⼊channel所对应的EventLoop的执⾏队列,⽽后者会导致线程的切换。
在writeAndFlush的底层,如果没有通过eventLoop执⾏的话,就会重新启动新的线程执⾏。
# 5.3、减少ChannelPipline的调⽤⻓度
public class YourHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// BAD (most of the times)
ctx.channel().writeAndFlush(msg);
// GOOD
ctx.writeAndFlush(msg);
}
}
前者是将msg从整个ChannelPipline中⾛⼀遍,所有的handler都要经过,⽽后者是从当前handler⼀直 到pipline的尾部,调⽤更短。
# 5.4、减少ChannelHandler的创建
如果channelhandler是⽆状态的(即不需要保存任何状态参数),那么使⽤Sharable注解,并在 bootstrap时只创建⼀个实例,减少GC。否则每次连接都会new出handler对象。
@ChannelHandler.Shareable
public class StatelessHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {}
}
public class MyInitializer extends ChannelInitializer < Channel > {
private static final ChannelHandler INSTANCE = new StatelessHandler();
@Override
public void initChannel(Channel ch) {
ch.pipeline().addLast(INSTANCE);
}
}
同时需要注意ByteToMessageDecoder之类的编解码器是有状态的,不能使⽤Sharable注解。
# 5.5、⼀些配置参数的设置
ServerBootstrap启动时,通常bossGroup只需要设置为1即可,因为ServerSocketChannel在初始化阶 段,只会注册到某⼀个eventLoop上,⽽这个eventLoop只会有⼀个线程在运⾏,所以没有必要设置为 多线程。⽽ IO 线程,为了充分利⽤ CPU,同时考虑减少线上下⽂切换的开销,通常设置为 CPU 核数的 两倍,这也是 Netty 提供的默认值。
在对于响应时间有⾼要求的场景,使⽤.childOption(ChannelOption.TCP_NODELAY, true) 和.option(ChannelOption.TCP_NODELAY, true)来禁⽤nagle算法,不等待,⽴即发送。
上次更新: 2023/08/12, 20:54:07