繁体   English   中英

Netty 4 - 池返回尚未准备好发送实际消息的通道

[英]Netty 4 - The pool returns a channel which is not yet ready to send the the actual message

我创建了一个 SimpleChannelInboundHandler 类型的入站处理程序并添加到管道中。 我的意图是每次建立连接时,我都想发送一个名为 session 的应用程序消息打开消息,并使连接准备好发送实际消息。 为了实现这一点,上面的入站处理程序覆盖了 channelActive(),其中 session 打开消息被发送,作为响应,我会收到 session 打开确认消息。 只有在那之后,我才能发送任意数量的实际业务消息。 我正在使用 FixedChannelPool 并初始化如下。 这在启动时运行良好。 但是如果远程主机关闭连接,之后如果调用下面的sendMessage()发送消息,则消息甚至在session打开消息之前通过channelActive()发送并获得其响应。 因此服务器忽略该消息,因为发送业务消息时 session 尚未打开。

我正在寻找的是,池应该只返回那些调用 channelActive() 事件的通道,该事件已经发送了 session 打开消息,并且它已经从服务器获得了 session 打开确认消息。 如何处理这种情况?

public class SessionHandler extends SimpleChannelInboundHandler<byte[]> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        if (ctx.channel().isWritable()) {
            ctx.channel().writeAndFlush("open session message".getBytes()).;
        }
    }
}

// At the time of loading the applicaiton
public void init() {
    final Bootstrap bootStrap = new Bootstrap();
    bootStrap.group(group).channel(NioSocketChannel.class).remoteAddress(hostname, port);
    fixedPool = new FixedChannelPool(bootStrap, getChannelHandler(), 5);

    // This is done to intialise connection and the channelActive() from above handler is invoked to keep the session open on startup
    for (int i = 0; i < config.getMaxConnections(); i++) {
        fixedPool.acquire().addListener(new FutureListener<Channel>() {
            @Override
            public void operationComplete(Future<Channel> future) throws Exception {
                if (future.isSuccess()) {

                } else {
                    LOGGER.error(" Channel initialzation failed...>>", future.cause());
                }

            }
        });
    }
}

//要实际发送消息,应用程序调用以下方法。

public void sendMessage(final String businessMessage) {
    fixedPool.acquire().addListener(new FutureListener<Channel>() {
        @Override
        public void operationComplete(Future<Channel> future) throws Exception {
            if (future.isSuccess()) {
                Channel channel = future.get();
                if (channel.isOpen() && channel.isActive() && channel.isWritable()) {
                    channel.writeAndFlush(businessMessage).addListener(new GenericFutureListener<ChannelFuture>() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if (future.isSuccess()) {
                                // success msg
                            } else {
                                // failure msg
                            }
                        }
                    });
                    fixedPool.release(channel);
                }
            } else {
                // Failure
            }
        }
    });
}

如果没有特定原因需要使用FixedChannelPool ,则可以使用另一个数据结构(List/Map)来存储 Channels。 您可以在发送 open session 消息后向数据结构中添加通道,并在channelInactive方法中将其删除。

如果您需要对频道执行批量操作,您可以使用ChannelGroup

如果您仍然希望使用FixedChannelPool ,您可以在通道中设置一个属性来确定是否发送了打开的消息:

ctx.channel().attr(OPEN_MESSAGE_SENT).set(true);

您可以在sendMessage function 中获取如下属性:

boolean sent = ctx.channel().attr(OPEN_MESSAGE_SENT).get();

channelInactive中,您可以将其设置为 false 或将其删除。

注意OPEN_MESSAGE_SENT是一个AttributeKey

public static final AttributeKey<Boolean> OPEN_MESSAGE_SENT = AttributeKey.valueOf("OPEN_MESSAGE_SENT");

我知道这是一个相当古老的问题,但我偶然发现了类似的问题,并不完全相同,但我的问题是Bootstrap.handler中的ChannelInitializer从未被调用。 解决方案是将管道处理程序添加到池处理程序的channelCreated方法中。

这是我现在可以使用的池定义代码:

pool = new FixedChannelPool(httpBootstrap, new ChannelPoolHandler() {
    @Override
    public void channelCreated(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(HTTP_CODEC, new HttpClientCodec());
        pipeline.addLast(HTTP_HANDLER, new NettyHttpClientHandler());
    }
    @Override
    public void channelAcquired(Channel ch) {
        // NOOP
    }
    @Override
    public void channelReleased(Channel ch) {
        // NOOP
    }
}, 10);

So in the getChannelHandler() method I assume you're creating a ChannelPoolHandler in its channelCreated method you could send your session message ( ch.writeAndFlush("open session message".getBytes()); ) assuming you only need to send the session创建连接时发送一次消息,否则,如果您需要每次发送 session 消息,您可以将其添加到channelAcquired方法。

暂无
暂无

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

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