网状的填坑——论填坑,我是专业的

  

1、为什么netty服务端启动后又无异常地自动退出?

由于代码1处执行完后直接进入2、3,那么netty服务端就会关闭退出。

解决一、直接在代码1后面处加上同步阻塞sync,那么只有服务端正常关闭channel时才会执行下面的语句

解决二、把代码2和3移到operationComplete里面,那么也只有channel关闭时才会让netty的两个线程组关闭


2、netty客户端连接池资源OOM

生产环境用netty作为客户端,为了提高性能,客户端与服务端创建多条链路,同时客户端创建一个TCP连接池。结果业务高峰期OOM

从异常日志和线程资源占用来看,导致内存泄漏的原因是应用创建了大量的EventLoopGroup线程池。这就是一个TCP连接对应一个NIO线程的模式。错误之在就是采用BIO模式来调用NIO通信框架,不仅没优化效果,还发生了OOM。

正确操作是

注意:Bootstrap自身不是线程安全的,但。connect方法它会创建一个新的NioSocketChannel,并从初始构造的EventLoopGroup中选择一个NioEventLoop线程执行真正的Channel连接操作,与执行Boostrap的线程无关。在同一个Boostrap创建多个客户端连接,EventLoopGroup是共享的,这些连接共用同一个NIO线程组EventLoopGroup,当某个链路发生异常或关闭时,只需要关闭并释放Channel本身即可,不能同时销毁NioEventLoop和所在线程组EventLoopGroup,


3、netty居然会产生内存池泄漏问题

在调用ctx.writeAndFlush方法时,当消息发送完成,Netty会主动帮助应用释放内存,内存释放场景如下

(1)如果是堆内存(PooledHeapByteBuf),则将HeapByteBuffer转换成DirectByteBuffer,并释放PooledHeapByteBuf到内存池。

(2)如果是DirectByteBuffer,则不需要转换,在消息发送完成后,由ChannelOutboundBuffer的remove方法负责释放


为了在实际项目中更好地管理ByteBuf,下面分4种场景说明

(1)基于内存池的请求ByteBuf,这类主要包括PooledDirectByteBuf和PooledHeapByteBuf,它由NioEventLoop线程在处理Channel读操作时分配,需要在业务ChannelInboundHandler处理完请求消息后释放(通常在解码之后),它的释放策略如下:

  1. ChannelInboundHandlerSimpleChannelInboundHandler,实现它的抽象方法channelRead0,ByteBuf的释放业务不用关心,由SimpleChannelInboundHandler负责释放

  2. 在业务ChannelInboundHandler中调用ctx.fireChannelRead(msg),让请求继续向后执行,直至调用DefaultChannelPipeline的内部类TailContext,由它负责释放请求信息

  3. 直接调用在channelRead方法里调用ReferenceCountUtil.release(reqMsg)

(2) 基于非内存池的请求ByteBuf,它也是需要按照内存池的方式释放内存

(3)基于内存池的响应ByteBuf,根据之前的分析,只要调用了writeAndFlush或flush方法,在消息发送完成后都会由Netty框架进行内存释放,业务不需要主动释放内存

(4)基于非内存池的响应ByteBuf,业务不需要主动释放内存


当然非内存池也不一定要手动释放,但最好手动释放。Netty里有4种主力的ByteBuf,其中UnpooledHeapByteBuf底下的byte[]能够依赖JVM GC自然回收;而UnpooledDirectByteBuf底下是DirectByteBuffer,是Java堆外内存,除了等JVM GC,最好也能主动进行回收,否则导致内存不足也产生内存泄露的假象;而PooledHeapByteBuf和PooledDirectByteBuf,则必须要主动将用完的byte[]/ByteBuffer放回池里,否则内存就要爆掉。


对于内存池泄露可以的监控可以配置启动参数

不同参数信息如下:

  1. DISABLED 完全关闭内存泄露检测,并不建议

  2. SIMPLE 以1%的抽样率检测是否泄露,默认级别

  3. ADVANCED 抽样率同SIMPLE,但显示详细的泄露报告

  4. PARANOID 抽样率为100%,显示报告信息同ADVANCED

最后,悄悄告诉你,网上的你些netty入门demo大都存在内存池泄露问题,只不过数据量传输少,可能运行大半年才会出现LEAK,就连《netty权威指南》入门demo也存在这个问题,也许就只是个入门demo,所以不弄得太复杂。什么你不信,你可以在入门demo的TimeClientHandler或TimeServerHandler加上下面这坨代码。

        ByteBuf firstMessage=null;
  for (int j=0;j & lt;Integer.MAX_VALUE;j + +) {
  firstMessage=Unpooled.buffer (1024);
  for (int i=0;我& lt;firstMessage.capacity ();我+ +){
  firstMessage.writeByte((字节)我);
  }
  ctx.writeAndFlush (firstMessage);
  }

网状的填坑——论填坑,我是专业的