发现问题
在使用中设备异常断开,InterceptHandler中的onConnectionLost()。经过调试发现是MoquetteIdleTimeoutHandler中的代码导致的,代码如下:
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState e = ((IdleStateEvent) evt).state();
if (e == IdleState.READER_IDLE) {
LOG.info("Firing channel inactive event. MqttClientId = {}.", NettyUtils.clientID(ctx.channel()));
// fire a channelInactive to trigger publish of Will
ctx.fireChannelInactive();
ctx.close().addListener(CLOSE_ON_FAILURE);
}
}
......
}
这部分代码的大致含义是:当在一段时间内没有收到任何数据后,就会调用触发ChannelInactive事件然后关掉连接。
在netty中事件都是在handler链中依次传递的。ChannelInactive事件最后传递到NettyMQTTHandler。处理逻辑如下:
public void channelInactive(ChannelHandlerContext ctx) {
String clientID = NettyUtils.clientID(ctx.channel());
if (clientID != null && !clientID.isEmpty()) {
LOG.info("N otifying connection lost event. MqttClientId = {}", clientID);
m_processor.processConnectionLost(clientID, ctx.channel());
}
ctx.close().addListener(CLOSE_ON_FAILURE);
}
如果条件成立,会调用一次m_processor.processConnectionLost(clientID, ctx.channel());这会导致InterceptHandler中的onConnectionLost()调用一次。因为连接紧接着又被关闭了,连接关闭同样会导致ChannelInactive事件,因此以上方法又会被触发一次,因此这样就会造成异常断开会调用两次onConnectionLost()。
解决方法
添加handlerRemove
@Override
public void channelInactive(ChannelHandlerContext ctx) {
/** modify by ljq 2018.6.11 会导致processConnectionLost调用两次*/
// String clientID = NettyUtils.clientID(ctx.channel());
// if (clientID != null && !clientID.isEmpty()) {
// LOG.info("N otifying connection lost event. MqttClientId = {}", clientID);
// m_processor.processConnectionLost(clientID, ctx.channel());
// }
ctx.close().addListener(CLOSE_ON_FAILURE);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
String clientID = NettyUtils.clientID(ctx.channel());
if (clientID != null && !clientID.isEmpty()) {
LOG.info("Notifying connection lost event. MqttClientId = {}", clientID);
m_processor.processConnectionLost(clientID, ctx.channel());
}
}
解释:
handler remove会在该handler从链中移除掉时被调用,一般的话没有手动从链中删除时,会在连接断开后回调该方法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。