Netty提供了 IdleStateHandler、ReadTimeoutHandler、WriteTimeoutHandler 对连接进行检测,一般心跳连接使用 IdleStateHandler 进行处理,源码分析也以 IdleStateHandler 进行讲解,以心跳连接章节的代码案例进行演示
名称 | 作用 |
---|---|
IdleStateHandler | 当连接的空闲时间(读或者写)太长时将会触发一个IdleStateEvent事件。可以在下一个继承ChannelInboundHandlerAdapter的handler(例如业务处理handler)中重写userEventTrigged方法来处理该事件 |
WriteTimeoutHandler | 当一个写操作不能在一定的时间内完成时抛出此异常并关闭连接。可以在下一个继承ChannelInboundHandlerAdapter的handler(例如业务处理handler)中重写exceptionCaught方法中处理这个异常 |
ReadTimeoutHandler | 如果在指定的事律没有发生读事件就会抛出这个异常并自动关闭这个连接。可以在下一个继承ChannelInboundHandlerAdapter的handler(例如业务处理handler)中重写exceptionCaught方法中处理这个异常 |
四大参数:
源码内主要包含四个参数,含义分别为
private final boolean observeOutput; 是否考虑出站较慢的情况,默认false
private final long readerIdleTimeNanos; 读事件空闲的时间,为0时不检测读事件空闲
private final long writerIdleTimeNanos; 写事件空闲的时间,为0时不检测写事件空闲
private final long allIdleTimeNanos; 读写事件空闲的时间,为0时不检测读写事件空闲
初始化:
当IdleStateHandler被添加到 pipeline 时,将会触发 initialize 方法
具体步骤:
1. 将 state 设置为1(防止重复初始化)
2. 调用 initOutputChanged_(_ctx_)_; 方法,初始化“监控出站数据属性”
3. 如果有进行自定义检测时间操作,调用 eventLoop 的 schedule 方法将定时任务添加到队列中
定时任务解析:
三个定时任务的核心均为继承其内部类 AbstractIdleTask (模板模式)进行实现
ReaderIdleTimeoutTask解析:
- 获取指定的读空闲检测时间的纳秒数
- 如果读取操作结束了,就用给定时间减去当前时间减去最后一次读时间,如果小于0就触发事件。反之继续放入队列。间隔时间是新的计算时间
- 首先将任务再次放到队列时间是刚开始设置的时间,返回一个promise 对象,用于做取消操作。然后设置 first 属性为 false ,表示下一次读取不再是第一次了,这个属性在 channelRead 方法会被改成 true
- 创建一个IdleStateEvent类型的写事件对象,将此对象传递给下一个Handler的UserEventTniggered 方法完成触发事件的操作
- 如果有读事件触发,则再添加下一次定时检测任务
ReaderIdleTimeoutTask方法详情
总结:
每次读取操作都会记录一个时间,定时任务时间到了,会计算当前时间和最后一次读的时间的间隔,如果间隔超过了设置的时间就触发UserEventTriggered方法
WriterIdleTimeoutTask解析:
相较于ReaderIdleTimeoutTask内的实现流程基本没有区别,多出一个是否出站速度慢(hasOutputChanged方法)的判断
channelIdle方法执后将对象传递给下一个Handler的 UserEventTniggered 方法
AllIdleTimeoutTask解析:
相较于ReaderIdleTimeoutTask内的实现流程基本没有区别,主要区别在与计算超时时间的规则略有不同
计算规则为当前时间减去最后一次读或写事件发生的时间
AllIdleTimeoutTask方法详情
总结:
- IdleStateHandler 可以实现心跳功能,当服务器和客户端没有任何读写交互时,并超过了给定的时间、则会触发用户 handler 的 userEventTriggered 方法。用户可以在这个方法中尝试向对方发送信息,如果发送失败,则关闭连接
- IdleStateHandler 的实现基于 EventLoop 的定时任务,每次读写都会记录-一个值,在定时任务运行的时候,通过计算当前时间和设置时间和上次事件发生时间的结果,来判断是否空闲
- 内部有3个定时任务,分别对应读事件,写事件,读写事件。通常用户监听读写事件就足够了
- IdleStateHandler 内部也考虑了-些极端情况:客户端接收缓慢,一次接收数据的速度超过了设置的空闲时间。Netty通过构造方法中的 observeOutput 属性来决定是否对出站缓冲区的情况进行判断
- Netty 不认为出站缓慢属于空闲,因此不触发空闲事件。但第一次出站会触发空闲检测,因为首次无法判断是出站缓慢还是空闲,如果出站缓慢的话可能造成 OOM , OOM 比空闲的问题更大。因此当应用出现了内存溢出、OOM之类并且写空闲极少发生(使用了observeOutput 为true) ,需要注意是不是数据出站速度过慢
- ReadTimeoutHandler 继承自 IdleStateHandler, 当触发读空闲事件时触发 ctxfireExceptionCaught 方法,并传入一个 ReadTimeoutException, 然后关闭Socket
- WriteTimoutHandler 的实现不是基于 IdleStateHandler 的,其原理是当调用 write方法 的时候会创建一个定时任务,任务内容是根据传入的 promise 的完成情况来判断是否超出了写的时间。当定时任务根据指定时间开始运行,发现 promise 的 isDone 方法返回 false 表明还没有写完,说明超时 > 抛出异常。当 wite 方法完成后,会打断定时任务