- pipeline的添加是有顺序的,如果用到IdleStateHandler,它需要添加到第一个
channel.pipeline()
.addLast(new IdleStateHandler(0, 0, 500, TimeUnit.MILLISECONDS))
.addLast(new HttpServerCodec());
- 如果handler继承自SimpleChannelInboundHandler,那么通过channelRead0在读取消息的时候是自动释放消息对象的,下面代码是SimpleChannelInboundHandler的处理过程
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (this.acceptInboundMessage(msg)) {
this.channelRead0(ctx, msg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (this.autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
protected abstract void channelRead0(ChannelHandlerContext var1, I var2) throws Exception;
- Netty的零拷贝带来高性能的同时,会带来对象释放的问题,需要开发者手动通过
ReferenceCountUtil.release();
释放,因为这些对象不是JVM堆内对象,垃圾回收程序不会自动回收这部分对象
- 如果有多个自定义pipeline,需要通过
ctx.fireChannelRead();
进行消息传递,否则消息不会传递给下一层的pipeline
- 由于大量JVM堆外对象的使用,极易出现内存泄露的问题,可以通过
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
这个设置,系统会捕捉内存泄露
- 如果想实现定时执行任务,类似心跳的处理,可以使用IdleStateHandler,这个handler会根据配置的链接读写情况,按设定的时间定时执行,IdleStateHandler是时间轮算法实现,效率非常高,占用资源很少
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.ALL_IDLE) {
}
} else {
super.userEventTriggered(ctx, evt);
}
}
- 可以在下面的pipeline中实现协议解析
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024 * 4000, 4, 4, 0, 0));
- ByteBuf默认是使用PooledByteBufAllocator来实现的,不需要池化的场景用
Unpooled.copiedBuffer();
- 回写数据的时候最好判断下channel状态以及是否可写,否则有可能写数据失败
if (ctx.channel().isActive() && ctx.channel().isWritable()) {
}
- Netty在writeAndFlush是异步写入,如果需要查看是否成功,需要注册一个ChannelFutureListener,以便在操作完成时获得通知
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
} else {
}
}
});
- 定时任务的另一种实现
if(scheduledFuture == null){
scheduledFuture = ctx.channel().eventLoop().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
businessTask(ctx);
}
}, 0, 500, TimeUnit.MILLISECONDS);
}
如果需要停止定时任务,调用
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
- 在pipeline内都是线程安全的,在某个pipeline内,不用考虑和并发的问题。最重要的一点,特别注意,不要在pipeline内执行耗时任务,否则eventLoop会被阻塞,造成其他连接对应的pipeline无法执行,切切切。标准的操作是把耗时任务丢给其他线程处理,等下个eventLoop再查询执行结果。