【问题标题】:Implementing keep-alive messages in Netty using WriteTimeoutHandler使用 WriteTimeoutHandler 在 Netty 中实现保活消息
【发布时间】:2019-07-17 19:32:06
【问题描述】:

我使用的是 Netty 3.2.7。我正在尝试在我的客户端中编写功能,以便如果在一定时间(例如 30 秒)后没有写入任何消息,则会向服务器发送一条“保持活动状态”消息。

经过一番挖掘,我发现 WriteTimeoutHandler 应该使我能够做到这一点。我在这里找到了这个解释:https://issues.jboss.org/browse/NETTY-79

Netty 文档中给出的例子是:

public ChannelPipeline getPipeline() {
     // An example configuration that implements 30-second write timeout:
     return Channels.pipeline(
         new WriteTimeoutHandler(timer, 30), // timer must be shared.
         new MyHandler());
 }

在我的测试客户端中,我就是这样做的。在 MyHandler 中,我还重写了 exceptionCaught() 方法:

public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
    if (e.getCause() instanceof WriteTimeoutException) {
        log.info("Client sending keep alive!");
        ChannelBuffer keepAlive = ChannelBuffers.buffer(KEEP_ALIVE_MSG_STR.length());
        keepAlive.writeBytes(KEEP_ALIVE_MSG_STR.getBytes());
        Channels.write(ctx, Channels.future(e.getChannel()), keepAlive);
    }
}

无论客户端在多长时间内不向通道写入任何内容,我覆盖的 exceptionCaught() 方法都不会被调用。

看WriteTimeoutHandler的源码,它的writeRequested()实现是:

public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
        throws Exception {

    long timeoutMillis = getTimeoutMillis(e);
    if (timeoutMillis > 0) {
        // Set timeout only when getTimeoutMillis() returns a positive value.
        ChannelFuture future = e.getFuture();
        final Timeout timeout = timer.newTimeout(
                new WriteTimeoutTask(ctx, future),
                timeoutMillis, TimeUnit.MILLISECONDS);

        future.addListener(new TimeoutCanceller(timeout));
    }

    super.writeRequested(ctx, e);
}

这里,似乎这个实现说,“当请求写入时,进行新的超时。当写入成功时,取消超时。”

使用调试器,看起来确实是这样。写入完成后,超时将被取消。这不是我想要的行为。我想要的行为是:“如果客户端在 30 秒内没有向通道写入任何信息,则抛出 WriteTimeoutException。”

那么,这不是 WriteTimeoutHandler 的用途吗?这就是我从网上阅读的内容中解释它的方式,但实现似乎并没有以这种方式工作。我用错了吗?我应该使用其他东西吗?在我试图重写的同一个客户端的 Mina 版本中,我看到 sessionIdle() 方法被重写以实现我想要的行为,但是这个方法在 Netty 中不可用。

【问题讨论】:

    标签: java netty


    【解决方案1】:

    对于 Netty 4.0 和更新版本,您应该扩展 ChannelDuplexHandler,例如从 IdleStateHandler documentation 扩展:

     // An example that sends a ping message when there is no outbound traffic
     // for 30 seconds.  The connection is closed when there is no inbound traffic
     // for 60 seconds.
    
     public class MyChannelInitializer extends ChannelInitializer<Channel> {
         @Override
         public void initChannel(Channel channel) {
             channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(60, 30, 0));
             channel.pipeline().addLast("myHandler", new MyHandler());
         }
     }
    
     // Handler should handle the IdleStateEvent triggered by IdleStateHandler.
     public class MyHandler extends ChannelDuplexHandler {
         @Override
         public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
             if (evt instanceof IdleStateEvent) {
                 IdleStateEvent e = (IdleStateEvent) evt;
                 if (e.state() == IdleState.READER_IDLE) {
                     ctx.close();
                 } else if (e.state() == IdleState.WRITER_IDLE) {
                     ctx.writeAndFlush(new PingMessage());
                 }
             }
         }
     }
    

    【讨论】:

      【解决方案2】:

      我建议添加IdleStateHandler,然后添加您的自定义实现IdleStateAwareUpstreamHandler,它可以对空闲状态做出反应。这对我来说在许多不同的项目中都很有效。

      javadocs 列出了以下示例,您可以将其用作实现的基础:

      public class MyPipelineFactory implements ChannelPipelineFactory {
      
          private final Timer timer;
          private final ChannelHandler idleStateHandler;
      
          public MyPipelineFactory(Timer timer) {
              this.timer = timer;
              this.idleStateHandler = new IdleStateHandler(timer, 60, 30, 0);
              // timer must be shared.
          }
      
          public ChannelPipeline getPipeline() {
              return Channels.pipeline(
                  idleStateHandler,
                  new MyHandler());
          }
      }
      
      // Handler should handle the IdleStateEvent triggered by IdleStateHandler.
      public class MyHandler extends IdleStateAwareChannelHandler {
      
          @Override
          public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
              if (e.getState() == IdleState.READER_IDLE) {
                  e.getChannel().close();
              } else if (e.getState() == IdleState.WRITER_IDLE) {
                  e.getChannel().write(new PingMessage());
              }
          }
      }
      
      ServerBootstrap bootstrap = ...;
      Timer timer = new HashedWheelTimer();
      ...
      bootstrap.setPipelineFactory(new MyPipelineFactory(timer));
      ...
      

      【讨论】:

      猜你喜欢
      • 2012-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-03
      相关资源
      最近更新 更多