moquette改造筆記(四):解決InterceptHandler中的onConnectionLost()調用兩次

發現問題

在使用中設備異常斷開,InterceptHandler中的onConnectionLost()。通過調試發現是MoquetteIdleTimeoutHandler中的代碼致使的,代碼以下:ide

@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()。netty

解決方法

添加handlerRemovecode

@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從鏈中移除掉時被調用,通常的話沒有手動從鏈中刪除時,會在鏈接斷開後回調該方法。事件

相關文章
相關標籤/搜索