【发布时间】:2015-07-19 23:21:02
【问题描述】:
我正在尝试使用 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 获取 Channel:
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 并直接写入通道可能会出现问题。
【问题讨论】: