发现问题

在使用中设备异常断开,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从链中移除掉时被调用,一般的话没有手动从链中删除时,会在连接断开后回调该方法。


liumang
343 声望36 粉丝

一直在思考怎么结合自己擅长的知识做些什么。现在有了好主意坚持一年,看看会有什么改变,有什么美好的事情发生。