网络I/o编程模型22 netty的源码总结

一 netty启动流程

1.new NioEventLoopGroup(1):这个表示bossGroup事件组有1个线程可以指定,如果new NioEventLoopGroup() 则默认会有cpu核数*2 个线程,这样可以充分利用多核的优势。
DEFAULT_EVENT_LOOP_THREADS=Math.max(1,SystemPropertyUtil.getInt("io.netty.eventLoopThreads",NettyRuntime.availableProcessors()*2));
2.从NioServerSocketChannel的初始化过程中,可以看出,pipeline是一个双向链表,并且它本身初始化了head和tail,这里调用addLast方法,也就是将整个handler插入到tail的前面。
3.channelHandlerContext对象是channelHandler和channelpipeline之间的关联,每当有channelHandler添加到pipeline中时,都会创建Context。
4.创建2个EventLoopGroup线程池数组。默认数组大小为cpu*2,方便chooser选择线程时提供性能。
5.BootStrap将boss设置为group属性,将worker设置为children属性。
6.通过bind方法启动,内部重要方法为initAndRegister和dobind方法
7.initAndRegister方法会反射创建NioServerSocketChannel以及相关的NIO对象,pipeline,unsafe同时也为pipeline初始了head节点和tail节点。
8.在register方法调用doBind()方法,该方法会调用NioServerSocketChannel的doBind方法对JDK的channel和端口进行绑定,完成netty服务器的所有启动,并开始监听连接事件。

二  netty接收请求的流程

总体流程:

1.接受连接---->创建一个新的NioSocketChannel------->注册到一个worker EventLoop上 --> 注册selector Read事件。
2.服务器轮询Accept事件,获取事件后调用unsafe的read方法,这个unsafe是serversocket的内部类。
3.DoReadMessage 用于创建NioSocketChannel对象,该对象包装JDK的Nio channel客户端,该方法会像创建ServerSocketChannel类似创建pipeline、unsafe
4.下一步,执行pipeline.fireChannelRead()方法,并将自己绑定到一个chooser选择器选择的workerGroup中的一个EventLoop,并且注册一个0表示成功,但并没有注册读(1)事件。

 三  channelpipeline和channelhandlercontext和channelhandler关系

  3.1 关系

1.channelSocket与channelPipeline是1:1关系,而channelpipeline包含多个channelhandlercontext,每一个channelhandlercontext都包含一个channelhandler,内部的channelhandlercontext一起组成了双向链表,这些channelhandlercoantext封装了我们编写addLast方法添加的channelhandler。

2.当一个请求进来的时候,会进入socket对应的pipeline,并经过pipeline所有的handler,这就是设计模式中过滤器模式。

 3.2  channelpipeline

该接口继承了inBound,outBound,Iterable接口,表示它可以调用数据出站和入站的方法。

1.首先,当一个请求进来的时候,会第一个调用pipeline的相关方法,如果是入站事件,则这些方法都是fire开头,表示开始管道的流动,让后面的handler继续进行处 。

2.pipeline 首先会调用Context的静态方法 fireXXX,并传入Context
3.然后,静态方法调用Context的invoke方法,而invoker方法内部会调用该context所包含的handler的真正的xxx方法,调用结束后,如果还需要继续向后传递,就调用context的firexxx2方法,循环往复。

 3.3  channelhandler

channelhandler其作用就是处理IO事件或拦截IO事件,并将其转发给下一个处理程序ChannelHandler。handler处理事件发呢我出站和入站,两个方向的操作都是不同的,因此,netty定义了两个子接口继承channnelhandler。

1.channelInboundhandler  入站事件接口

2.channelOutboundHndler  出站事件接口
bind 方法:当请求将channel绑定到本地地址时调用
close 方法: 当请求关闭channel 时调用等等。

3.4 channelhandlercontext

channelHandlerContext 继承了出站方法调用接口和入站方法调用接口,如下图所示

 任何一个channelSocket 创建的同时,都会常见一个pipeline;
当用户或者系统内部调用pipeline的add**方法添加handler时,都会创建一个包装这个handler的Context。这些Context在peipeline中组成双向链表

3.5 channelhandlercontext调用channelhandler的流程

1.每当创建channelSocket的时候都会创建一个绑定的pipeiline,1:1的关系,创建peipeline的时候会创建tail节点和head节点,形成最初的链表。
2.在调用peipeline的addLast方法的时候,会根据给定的handler创建一个context,然后建这个context插入到链表的尾端(tail的前面)
3.context包装了多个handeler,多个context在pipeline中形成双向链表。
4.入站方向叫inbound,由head节点开始,出站方向叫outbound,由tail节点开始。
因为出站是从内部向外面进行写操作,从tail开始能够让前面的handler进行处理,防止handler被遗漏,如编码。
入站当然就是从head网内部输入,让后面的handler能够处理这些输入的数据。比如解码。
注意:虽然head也实现了outbound接口,但不是从head开始执行出站任务的。
5.而节点中间的传递通过AbstaractChannelHandlerContext类内部的fire系列方法,找到当前节点的下一个节点不断的循环播放,是一个过滤器形成 完成对handler的调度。

四 netty的心跳源码

4.1常见心跳处理器

netty提供了IdleStateHandler,ReadTimeoutHandler,WriteTimeoutHandler这个3个handler检测连接的有效性。
IdleStateHandler:    当连接的空闲时间(读或者写)太长时,将会触发一个IdleStateEvent事件。然后,你可以通过你的ChannelInboundHandler中重写userEventTrigged方法来处理事件。
ReadTimeoutHanler:   如果在制定的事件没有发生读事件,就会抛出这个异常,并自动关闭连接。你同样可以在exceptionCaught方法中处理异常。
WriteTimeoutHandler: 当一个写操作不能在一定的时间完成时,抛出此异常,并关闭连接,你同样可以在exceptionCaught方法中处理这个异常。
中ReadTimeout事件和WriteTimeout事件都会自动关闭连接,而且属于异常处理。

4.2 IdleStateHandler事件常用方法

1.private final  boolean  observeOutput;//是否考虑出站时较慢的情况,默认值为false;
2.private final  long   readerIdleTimeNanos;//读事件空闲时间,0则禁用事件
3.private final long writerIdleTimeNanos;//写事件空闲时间,0则禁用事件
4.private final long alldleTimeNanos;//读或写空闲时间,0则禁用事件

4.3 IdleStateHandler的作用

1.IdleStateHandler 可以实现心跳功能,当服务器和客户端没有任何读写交互时,并超过了给定的时间,则会触发用户的handler的userEventTriggered方法
用户可以在这个方法中尝试向对方发送信息,如果发送失败,则关闭连接。
2.IdleStateHandler的实现基于EventLoop的定时任务,每次读写都会记录一个值,在定时任务运行的时候通过计算当前时间和设置时间和上次事件发生时间的结果,来判断是否空闲。

五 EventLoop的作用

每次执行execute方法都是向队列中添加任务。当第一次添加时就启动线程,执行run方法,而run方法是整个EventLoop的核心,就像EventLoop的名字一样,Loop是不停循环。
做3件事情:
1.调用selector的select方法,默认阻塞1秒钟,如果有定时任务,则在定时任务剩余时间的基础上再加上0.5秒进行阻塞。当执行execute方法的时候,也就是添加任务的时候,唤醒selector,防止selector阻塞时间过长。
2.当selector返回的时候,回调用processSelectedKeys方法 对selectKey进行处理。
3.当processSelectedKeys方法执行结束后,则按照ioRatio的比例执行runAllTasks方法,默认是IO任务时间和非IO任务时间是相同的,可以进行调优,比如非IO任务比较多,你就将ioRatio调小点,这样非IO任务就能执行的长一点,防止队列积攒过多的任务。

六 handler和context加入线程池

处理耗时业务分为两种方式:handler加入线程池和context中加入线程池

1.在handler中添加异步操作,更加灵活方便。

2.是netty标准的方式(加入到队列中),但是会将整个handler都交个业务线程池,不论耗时不耗时,都加入到队列里,不够灵活。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值