moquette改造筆記(四):解決InterceptHandler中的onConnectionLost()呼叫兩次
阿新 • • 發佈:2018-12-10
發現問題
在使用中裝置異常斷開,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從鏈中移除掉時被呼叫,一般的話沒有手動從鏈中刪除時,會在連線斷開後回撥該方法。