具有netty

时间:2015-07-19 23:21:03

标签: java netty

我正在尝试使用netty 4.1编写非阻塞代理。我有一个处理传入连接的“FrontHandler”,然后是一个处理传出连接的“BackHandler”。我正在关注HexDumpProxyHandler(https://github.com/netty/netty/blob/ed4a89082bb29b9e7d869c5d25d6b9ea8fc9d25b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java#L67

在这段代码中,我找到了:

@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
    if (outboundChannel.isActive()) {
        outboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {, I've seen:

表示仅在出站客户端连接已准备好时才写入传入消息。这在HTTP代理案例中显然不理想,所以我在想什么是处理它的最佳方法。

我想知道是否禁用前端连接上的自动读取(并且只有在外发客户端连接准备就绪时才触发读取)是一个不错的选择。然后,我可以在后端处理程序的“channelActive”事件中再次对子套接字启用autoRead。但是,我不确定每次“read()”调用会在处理程序中获得多少消息(使用HttpDecoder,我假设我会得到最初的HttpRequest,但我真的想避免获得后续的HttpContent / LastHttpContent消息,直到我再次手动触发read()并通过通道启用autoRead。

另一个选择是使用Promise从客户端ChannelPool获取频道:

private void setCurrentBackend(HttpRequest request) {
    pool.acquire(request, backendPromise);

    backendPromise.addListener((FutureListener<Channel>) future -> {
        Channel c = future.get();
        if (!currentBackend.compareAndSet(null, c)) {
            pool.release(c);
            throw new IllegalStateException();
        }
    });
}

然后通过该承诺从输入复制到输出。例如:

private void handleLastContent(ChannelHandlerContext frontCtx, LastHttpContent lastContent) {
    doInBackend(c -> {
        c.writeAndFlush(lastContent).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                future.channel().read();
            } else {
                pool.release(c);
                frontCtx.close();
            }
        });
    });
}
private void doInBackend(Consumer<Channel> action) {
    Channel c = currentBackend.get();
    if (c == null) {
        backendPromise.addListener((FutureListener<Channel>) future -> action.accept(future.get()));
    } else {
        action.accept(c);
    }
}

但是我不确定将承诺永远保存在那里有多好,并且通过添加监听器来完成从“前”到“后”的所有写入。我也不确定如何实现承诺,以便在正确的线程中执行操作......现在我正在使用:

backendPromise = group.next().<Channel> newPromise(); // bad
// or
backendPromise = frontCtx.channel().eventLoop().newPromise(); // OK?

(其中group与前端的ServerBootstrap中使用的eventLoopGroup相同)。

如果它们没有通过正确的线程处理,我认为在“doInBackend”方法中进行“else {}”优化可能会有问题,以避免使用Promise并直接写入通道。

2 个答案:

答案 0 :(得分:2)

no-autoread方法本身不起作用,因为即使只执行了一个read(),HttpRequestDecoder也会创建几条消息。

我已经使用链式CompletableFutures解决了这个问题。

答案 1 :(得分:0)

我参与了基于MQTT协议的类似代理应用程序。所以它基本上用于创建实时聊天应用程序。我必须设计的应用程序本质上是异步的,所以我自然不会遇到任何这样的问题。因为如果

outboundChannel.isActive() == false

然后我可以简单地将消息保存在队列或持久数据库中,然后在outboundChannel启动后处理它们。但是,由于您正在讨论HTTP应用程序,因此这意味着应用程序本质上是同步的,这意味着客户端无法继续发送数据包,直到outboundChannel启动并运行。因此,您建议的选项是只有在通道处于活动状态时才会读取数据包,您可以通过禁用ChannelConfig中的自动读取来手动处理消息读取。

但是,我想建议你应该检查outboundChannel是否有效。如果通道处于活动状态,则向前发送数据包以进行处理。如果频道未激活,您应该通过发回类似 Error404

的响应来拒绝该数据包

除此之外,您应该将客户端配置为在一定时间间隔后继续重试发送数据包,并相应地处理需要执行的操作,以防通道花费太长时间变为活动状态并变得可读。手动处理channelRead通常不是优选的并且是反模式。您应该让Netty以最有效的方式为您处理。