美文网首页
Netty 使用心得

Netty 使用心得

作者: 包_包 | 来源:发表于2017-07-01 15:37 被阅读0次

    目前在java socket服务器开发 基本上都是基于MINA 和 Netty.

    所以这2个东西是非常的重要.

    MINA不在本次说明的范围之内,有兴趣可以自行脑补(会用netty了,MINA基本上没问题.)

    下面咱们说一下Netty,纵观市面上 Netty3X-Netty4X-Netty5X。一时让人摸不到头绪

    我之前用的是Netty3X,后来看了一下Netty4X.真心发现坑很多。所以在这里记录一下.有助大家一起进步学习

    主要看了2块内容 线程模型和PooledBytebuf

    先说一下线程模型

    Netty3X的线程模型

    Netty 3.X的I/O操作线程模型比较复杂,它的处理模型包括两部分:

    1:Inbound:主要包括链路建立事件、链路激活事件、读事件、I/O异常事件、链路关闭事件等;

    2:Outbound:主要包括写事件、连接事件、监听绑定事件、刷新事件等。

    我们首先分析下Inbound操作的线程模型:

    Netty 3 Inbound操作线程模型

    从上图可以看出,Inbound操作的主要处理流程如下:

    1:I/O线程(Work线程)将消息从TCP缓冲区读取到SocketChannel的接收缓冲区中;

    2:由I/O线程负责生成相应的事件,触发事件向上执行,调度到ChannelPipeline中;

    3:I/O线程调度执行ChannelPipeline中Handler链的对应方法,直到业务实现的Last Handler;

    4:Last Handler将消息封装成Runnable,放入到业务线程池中执行,I/O线程返回,继续读/写等I/O操作;

    5:业务线程池从任务队列中弹出消息,并发执行业务逻辑。

    通过对Netty 3的Inbound操作进行分析我们可以看出,Inbound的Handler都是由Netty的I/O Work线程负责执行

    大概分析如下 Work线程(IO)收到消息各种decode + Hander以后 push到业务线程池

    EndHandler以后 切换到 业务线程池这块内容.对于我的项目.我是自己实现的。

    下面我们继续分析Outbound操作的线程模型:

    Netty 3 Outbound操作线程模型

    从上图可以看出,Outbound操作的主要处理流程如下:

    1:业务线程发起Channel Write操作,发送消息;

    2:Netty将写操作封装成写事件,触发事件向下传播;

    3:写事件被调度到ChannelPipeline中,由业务线程按照Handler Chain串行调用支持Downstream事件的Channel Handler;

    4:执行到系统最后一个ChannelHandler,将编码后的消息Push到发送队列中,业务线程返回;

    5:Netty的I/O线程从发送消息队列中取出消息,调用SocketChannel的write方法进行消息发送。

    大概的分析流程就是我们在一个地方调用channel.write方法(业务线程中)其实一套的Handler+数据Encode是在业务线程中调用的,

    全部完成以后发送到IO线程(这块和用户没关系).

    所以在Netty3中 我们其实最最最关心的是收数据这块(消息吞吐.消息分发等).对于发数据其实没什么关心的.毕竟是在业务线程

    Netty 4.X 版本线程模型

    Netty 4 Inbound和Outbound操作线程模型

    从上图可以看出,Outbound操作的主要处理流程如下:

    I/O线程NioEventLoop从SocketChannel中读取数据报,将ByteBuf投递到ChannelPipeline,触发ChannelRead事件;

    I/O线程NioEventLoop调用ChannelHandler链,直到将消息投递到业务线程,然后I/O线程返回,继续后续的读写操作;

    业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送;

    如果是由业务线程发起的写操作,ChannelHandlerInvoker将发送消息封装成Task,放入到I/O线程NioEventLoop的任务队列中,由NioEventLoop在循环中统一调度和执行。放入任务队列之后,业务线程返回;

    I/O线程NioEventLoop调用ChannelHandler链,进行消息发送,处理Outbound事件,直到将消息放入发送队列,然后唤醒Selector,进而执行写操作。

    通过流程分析,我们发现Netty 4修改了线程模型,无论是Inbound还是Outbound操作,统一由I/O线程NioEventLoop调度执行

    Note:

    可以看到在Netty4X版本中 所有的 Encode Decode Handler 都在IO线程中调用了.原来的Wirte方法.会在业务线程封装成Task push到IO线程

    所以这里对于业务会有一定的影响(PooledByteBuf的内存泄漏,耗时方法等) 都是很危险的.

    但是除了缺点当然也有优点(以前业务线程池调用channel.write的时候)对于业务线程池可能出现多个线程同时访问一个实例的情况

    所以要用synchronized修饰封装的Write方法

    我这里在Read进入业务线程的时候 使用FSM 进行了 TryLock.所以在一个基类的源头就Cut了这种情况.

    但是在Netty4X里面.我想大家可能就不必太Care这个问题了.毕竟Write这块是Push到PipeLineChannel线程中成为消息了.已经大大的降低了出现问题的情况(因为我用了FSM+TryLock)所以这块没做处理.


    Pooledbytebuffer 使用

    这个东西没怎么太关注过.但是实际使用的时候经常问题多多.所以记录一下自己对这东西的理解

    1:在4X初始化的时候做一个配置可以提高效率

    主要是加了以下两句:

    bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

    bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//关键是这句

    https://github.com/netty/netty/issues/3319---good case

    2:以前在业务线程使用Bytebuf的.直接移植会造成内存泄漏

    3:ReferenceCountUtil.release(msg); 和 SimpleChannelInboundHandler的关系 

    http://blog.csdn.net/linuu/article/details/51307060 一个我们需要注意的case(当然直接 netty + protobuf)可能已经会避免这些坑了

    4:2进制数据处理 处理需谨慎哈哈

    由于我的项目里 编码解码大部分业务都交由业务线程处理了.所以大家也可以这么做. Netty只做底层该做的....只是个人看法共勉

    相关文章

      网友评论

          本文标题:Netty 使用心得

          本文链接:https://www.haomeiwen.com/subject/lwgwcxtx.html