-----心跳源码分析-----
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完成后,会打断定时任务;