2012-04-12 144 views
0

最後編輯/結論的Netty的messageReceived()將不會被調用與長壽連接

一段時間後,這是無關的網狀問題,仍然很難調試。 messageReceived中的工作線程有時會被阻塞,所以一段時間後池中沒有線程可用。

在我的公司,我們使用的是網狀偵聽連接形式GPS跟蹤設備原來的問題。跟蹤器通過GPRS進行通信。

我們經歷了netty 3.2.4-final的非常奇怪的行爲。

經過一段時間(我不能確切地說出多少,但接近一天),我們沒有收到跟蹤器發送的任何消息。這意味着我們實現SimpleCahnnelUpstreamHandler的messageReceived方法將不會被調用!但是,如果我使用tcpdump捕獲所有數據包,則可以看到所有消息進入!

這是一個已知問題,已經在更高版本的netty中修復了嗎?

我們的渠道管道是這樣的:

... 
final TcpListenerChannelHandler tcpChannelHandler; 


@Inject 
public TcpListenerPipeline(TcpListenerChannelHandler tcpChannelHandler) { 
    this.tcpChannelHandler = tcpChannelHandler; 
} 

@Override 
public ChannelPipeline getPipeline() throws Exception { 
     ChannelPipeline p = Channels.pipeline(); 
     p.addLast("frameDecoder", new DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter())); 
     p.addLast("encoder", new ByteArrayWrapperEncoder()); 
     p.addLast("handler", tcpChannelHandler); 
     return p; 
} 
... 

我們實例監聽方式如下:

public void startListen() { 
     ChannelFactory channelFactory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool(),20); 
     bootstrap = new ServerBootstrap(channelFactory); 
     bootstrap.setPipelineFactory(pipeline); 
     bootstrap.setOption("child.tcpNoDelay", true); 
     bootstrap.setOption("child.keepAlive", true); 
     lazyLogger.getLogger().info("Binding Tcp listener to 0.0.0.0 on port '{}'", listenPort); 
     serverChannel = bootstrap.bind(new InetSocketAddress("0.0.0.0", listenPort)); 
} 

沒有任何人有什麼線索可能是錯的?或者我們應該手動斷開所有的頻道嗎?

編輯:

我對這個問題

當沒有消息被處理一些更多的信息,它也會發生channelConnected不叫成功的遠程連接。我遠程調試的問題,同時發現:

  • 在NioServerSocketPipelineSink.java線#246 registerAcceptedChannel(acceptedSocket,currentThread);發生
  • 軟件執行一直到 DefaultChannelPipeline行#781有不同的事件,但我的TcpListenerChannelHandler從來沒有在他的上下文中。

最奇怪的是,有時netty會注意到某個通道已連接,有時卻不會。

EDIT2:

TcpListenerCahnnelHandler是一個簡單的實現SimpleChannelUpstreamHandler

從它

亮點:

public class TcpListenerChannelHandler extends SimpleChannelUpstreamHandler { 
... 
@Override 
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 
    super.channelConnected(ctx, e); 
    _logger.info("{} device connected from: {}", deviceProtocol.getName(), ctx.getChannel().getRemoteAddress()); 
    deviceConnectionRegistry.channelConnected(ctx.getChannel()); 
} 

@Override 
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 
    super.channelDisconnected(ctx, e); 
    _logger.info("{} device from endpoint '{}' disconnected.", deviceProtocol.getName(), ctx.getChannel().getRemoteAddress()); 
    deviceConnectionRegistry.channelDisconnected(ctx.getChannel()); 
} 

@Override 
public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception { 
    super.messageReceived(ctx, messageEvent); 

    ... 
    NOTE: here we process the meassage, I do not think it can cause any problem 
} 


@Override 
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { 
     if(_logger.isWarnEnabled()) 
      _logger.warn(deviceProtocol.getName()+ " device" 
        +e.getChannel().getRemoteAddress()+" channel", e.getCause()); 

     if (!(e.getCause() instanceof ConnectException)) 
      e.getChannel().close(); 
} 

在我已經升級到3.3.1決賽的同時。如果問題再次發生,我有一些想法在哪裏繼續調試。

編輯3:

我已經升級到3.3.1最終,兩天後同樣的問題又發作了。

我不知道它是否相關,但我們在同一個物理接口上有更多的IP地址。我們是否應該嘗試只聽一個界面?有更多的eth接口有任何已知的問題嗎?

但同樣:tcpdump識別跟蹤器的消息,但netty不會在我的自定義處理程序中調用messageReceived。

EDIT 4:

我進一步調試的代碼。問題發生在NioWorker.java 在第131行(boolean offered = registerTaskQueue.offer(registerTask);)運行正常,但是這個任務永遠不會被處理。這意味着748行的RegisterTask.run()永遠不會被調用。

+0

是否可以包含您的自定義處理程序? – 2012-04-12 12:03:39

+0

我已經包含了TcpListenerChannelHandler的相關部分,這是唯一的自定義處理程序,這有助於嗎? – Szobi 2012-04-13 15:43:00

回答

1

不知道,你是否嘗試添加LoggingHandler來觀看所有內容? 我用它來使用自定義處理程序:

/** 
* 
* Adapted from the original LoggingHandler in Netty. 
*/ 
public class LoggingHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler { 

    String name; 
    boolean hexDump; 

    public LoggingHandler(String name, boolean hexDump) { 
     this.name = name; 
     this.hexDump = hexDump; 
    } 

    /** 
    * Logs the specified event to the {@link InternalLogger} returned by 
    * {@link #getLogger()}. If hex dump has been enabled for this handler, 
    * the hex dump of the {@link ChannelBuffer} in a {@link MessageEvent} will 
    * be logged together. 
    */ 
    public void log(ChannelEvent e) { 

     String msg = name + " >> " + e.toString(); 

     // Append hex dump if necessary. 
     if (hexDump && e instanceof MessageEvent) { 
      MessageEvent me = (MessageEvent) e; 
      if (me.getMessage() instanceof ChannelBuffer) { 
       ChannelBuffer buf = (ChannelBuffer) me.getMessage(); 
       msg = msg + " - (HEXDUMP: " + ChannelBuffers.hexDump(buf) + ')'; 
      } 
     } 

     // Log the message (and exception if available.) 
     if (e instanceof ExceptionEvent) { 
      Logger.debug(this, msg, ((ExceptionEvent) e).getCause()); 
     } else { 
      Logger.debug(this, msg); 
     } 

    } 



    public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) 
      throws Exception { 
     log(e); 
     ctx.sendUpstream(e); 
    } 

    public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) 
      throws Exception { 
     log(e); 
     ctx.sendDownstream(e); 
    } 

至極插在客戶端和服務器端。 在服務器端,我用它將它添加到子對象和父對象中:

ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), 
       Executors.newCachedThreadPool()); 
     ServerBootstrap bootstrap = new ServerBootstrap(factory); 
     bootstrap.setOption("child.tcpNoDelay", true); 
     bootstrap.setOption("child.keepAlive", true); 

     bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 

      public ChannelPipeline getPipeline() throws Exception { 
       ChannelPipeline pipeline = Channels.pipeline(); 
       pipeline.addLast("LOGGER", new LoggingHandler("SERVER", true)); 
       pipeline.addLast("LAUNCHER", handler.new OnChannelConnectedPlugger()); 
       return pipeline; 
      } 
     }); 
     bootstrap.setParentHandler(new LoggingHandler("SERVER-PARENT", true)); 
+0

感謝tipp,我會嘗試添加一些日誌記錄! – Szobi 2012-04-13 15:15:17