简体   繁体   English

使用WriteTimeoutHandler在Netty中实现keep-alive消息

[英]Implementing keep-alive messages in Netty using WriteTimeoutHandler

I am using Netty 3.2.7. 我使用的是Netty 3.2.7。 I am trying to write functionality in my client such that if no messages are written after a certain amount of time (say, 30 seconds), a "keep-alive" message is sent to the server. 我正在尝试在我的客户端编写功能,这样如果在一定时间(例如30秒)之后没有写入消息,则会向服务器发送“保持活动”消息。

After some digging, I found that WriteTimeoutHandler should enable me to do this. 经过一番挖掘,我发现WriteTimeoutHandler应该让我这样做。 I found this explanation here: https://issues.jboss.org/browse/NETTY-79 . 我在这里找到了这个解释: https//issues.jboss.org/browse/NETTY-79

The example given in the Netty documentation is: 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());
 }

In my test client, I have done just this. 在我的测试客户端,我做到了这一点。 In MyHandler, I also overrided the exceptionCaught() method: 在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);
    }
}

No matter what duration the client does not write anything to the channel, the exceptionCaught() method I have overridden is never called. 无论客户端在什么时间内没有向通道写入任何内容,我从未调用过覆盖的exceptionCaught()方法。

Looking at the source of WriteTimeoutHandler, its writeRequested() implementation is: 查看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);
}

Here, it seems that this implementation says, "When a write is requested, make a new timeout. When the write succeeds, cancel the timeout." 在这里,似乎这个实现说,“当请求写入时,进行新的超时。当写入成功时,取消超时。”

Using a debugger, it does seem that this is what is happening. 使用调试器,似乎这就是正在发生的事情。 As soon as the write completes, the timeout is cancelled. 写入完成后,超时将被取消。 This is not the behavior I want. 这不是我想要的行为。 The behavior I want is: "If the client has not written any information to the channel for 30 seconds, throw a WriteTimeoutException." 我想要的行为是:“如果客户端没有向通道写入任何信息30秒,则抛出WriteTimeoutException。”

So, is this not what WriteTimeoutHandler is for? 那么,这不是WriteTimeoutHandler的用途吗? This is how I interpreted it from what I've read online, but the implementation does not seem to work this way. 这就是我从在线阅读的内容中解释它的方法,但实现似乎并不是这样。 Am I using it wrong? 我用错了吗? Should I use something else? 我应该用别的吗? In our Mina version of the same client I am trying to rewrite, I see that the sessionIdle() method is overridden to achieve the behavior I want, but this method is not available in Netty. 在我试图重写的同一客户端的Mina版本中,我看到sessionIdle()方法被覆盖以实现我想要的行为,但是这种方法在Netty中不可用。

For Netty 4.0 and newer , you should extend ChannelDuplexHandler like in example from IdleStateHandler documentation : 对于Netty 4.0及更高版本 ,您应该像IdleStateHandler文档中的示例一样扩展ChannelDuplexHandler

 // 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());
             }
         }
     }
 }

I would suggest to add the IdleStateHandler and then add your custom implementation of IdleStateAwareUpstreamHandler which can react on the idle state. 我建议添加IdleStateHandler ,然后添加你的自定义实现IdleStateAwareUpstreamHandler它可以在空闲状态下的反应。 This works out very well for me on many different projects. 在许多不同的项目中,这对我来说非常好。

The javadocs list the following example, that you could use as the base of your implementation: 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));
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM