Netty源码--心跳源码阅读

-----心跳源码分析-----
1)目的:
  (1)Netty作为一个网络框架,提供了众多功能,比如:编码、解码等;
  (2)Netty还提供了非常重要的一个服务心跳机制heartbeat,通过心跳检查对方是否有效,
        这是RPC框架中必不可少的功能;

2)Nety中的3种Handler:
  (1)IdleStateHandler(重点): 
    当连接的空闲时间(读或写)太长时,将会触发一个IdleStateEvent事件,然后你可以通过你的ChannelInboundHandler,
      中重写userEventTriggered方法来处理该事件;

    1:纳秒: 单位是10亿分之1秒
    2:为什么超时的时候还要考虑出站慢的情况?
      假如:5s发送了一个东西, 我设置的超时事件是10s,那么不会触发读超时;
      但是:连接有效,但是网速非常慢,发送了20s才接收完毕,那么就触发了超时;
    3:handlerAdded:
      initialize:
        System.nanoTime()-->返回当前的时间,单位是纳秒

        开启定时任务:
          allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx), allIdleTimeNanos, TimeUnit,NANOSECONDS);
            其中: AllIdleTimeoutTask是一个内部类 extends AbstractIdleTask(提供了一个模板方法);
            通道关闭了就不执行任务了;

    4:核心流程
      1:得到用户设置的超时时间;
      2:如果读取操作结束了(执行了channelReadComplete方法设置),就用当前时间进去给定时间和最后一次读操作时间
          (执行了channelReadComplete方法设置),如果小于0,就触发事件. 反之,继续放入队列. 间隔事件是:新的计算时间;
      3:触发的逻辑是:首先将任务再次放入队列,事件是刚开始设置的时间,返回一个Promise对象,用于做取消操作. 
          然后设置first属性为false,表示下一次读取不是第一次了,这个属性在channelRead方法会被改为true;
      4:创建一个IdleStateEvent类型的写事件对象,将此对象传递给用户的UserEventTriggered方法,完成触发事件的操作;
        可以看出来回调的方式是-->调用约定的接口,而不是像js一样搞一个匿名函数!!!
      5:总的来说:每次读取操作都会记录一个时间,定时任务到了,会计算当前时间和最后一次时间读的时间的间隔,如果间隔超过了
          设置的时间,就会触发UserEventTriggered方法. 这个在介绍IdleStateHandler时说过.


  (2)ReadTimeoutHandler:
    如果在指定的时间没有发生读事件,就会抛出这个异常,并自动关闭这个连接,你可以在exceptionCaught方法中处理这个异常;

      1:核心流程:
          写任务的run代码逻辑基本和读任务逻辑一样,唯一不同的就是有一个针对出站慢数据的判断hasOutputChanged,
            因为这个读超时可能因为出站太慢造成的;
  (3)WriteTimeoutHandler:
    当一个写操作不能再一定的时间内完成时,抛出此异常,并关闭连接,你同样可以在exceptionCaught方法中处理这个异常;  

      1:核心流程:
        1:标识这个监控着所有的事件,当读写事件发生时,都会记录,代码逻辑和写事件的基本一致;
        2:需要注意的是
          long nextDelay = allIdleTimeNanos;
          if(!reading){
            nextDelay = ticksInNanos() - Math.max(lastReadTime, lastWriteTime);
          }
        3:这里的时间计算是读写事件中最大的值来的,然后像写事件一样,判断是否发生了写的慢的情况;


     schedule是任务队列。。。如何触发自定义事件???

3)Netty的心跳小结
  (1)IdleStateHandler可以实现心跳功能,当服务器和客户端没有任何读写交互时,并超过了给定的时间,则会触发用户Handler的
        的userEventriggered方法,用户可以在这个方法中尝试向对方发送信息,如果发送失败,则关闭连接;
  (2)IdleStateHandler的实现基于EventLoop的定时任务,每次读写都会记录一个值,在定时任务运行的时候,通过计算当前时间和
        设置时间和上次时间发生时间的结果,来判断是否空闲;
  (3)内部有3个定时任务,分别对应: 读事件、写事件、读写事件, 通常用户监听读写事件就够了;
  (4)同时,IdleStateHandler内部也考虑了一些极端情况,客户单接收缓慢,一次接收数据的速度超过了设置的空闲时间,Netty通过构造
       方法中的observeOutput属性来决定是否对出站缓冲区的情况进行判断;
  (5)如果出站缓慢,Netty不认为这是空闲,也不触发空闲事件,但第一次无论如何也是要触发的,茚虫威第一次无法判断是出站缓慢,还是空闲,
       当然出站缓慢的话,可能造成OOM,OOM比空闲的问题更大;
  (6)所以,当你的应用出现了内存溢出,OOM之类,并且写空闲极少发生(使用了observeOutput为true),那么就需要注意是不是数据出站速度
      过慢造成的;
  (7)还有一个注意的地方: 就是ReadTimeoutHandler, 它继承自IdleStateHandler,当触发读写空闲事件的时候,
      就触发ctx.fireExceptionCaught方法,并传入一个ReadTimeoutException,然后关闭Socket;
  (8)而WriteTimeoutHandler的实现不是基于IdleStateHandler的,它的原理是: 当调用write方法的时候,会创建一个定时任务,
       任务的内容是根据传入的Promise的完成情况来判断是否超出了写的时间。 当定时任务根据指定时间开始,发现Promise的
       isDone方法返回false,表明还没有写完,则说明超时了,则抛出异常,当write完成后,会打断定时任务;

 

你可能感兴趣的:(【netty】)