这与这个问题有关,但我试图将我的问题分解成更小的步骤。
我正在尝试使用接收http请求的netty编写一个简单的http服务器(服务器A),向另一台服务器(服务器B)发出http请求,然后将响应中的内容复制到对初始请求的响应中。我知道有一些如何做到这一点的例子,比如 LittleProxy,但代码相当复杂,而且由于我是 netty 的 n00b,我试图让我的第一个代码尽可能简单,而不会进入杂草。
现在,我忽略了所有关于并发的问题,只建立了一个从服务器 A 到服务器 B 的通道(我知道这会因并发请求而严重中断,但它使我的初始任务更简单)。
我的方法如下:
设置客户端引导程序并连接到在 localhost 端口 18080 上运行的服务器 B。获取相应的通道。
启动服务器 A 在端口 2080 上侦听,使用管道解码 http 请求,然后写入到服务器 B 的通道。
向生成的通道 future 添加一个侦听器,它将从服务器 B 的响应内容复制到对原始客户端对服务器 A 的请求的响应。
这是我的代码(非常短),我正在尝试完全按照我上面的描述进行操作。我的问题是我不知道如何将服务器 B 的响应复制到服务器的响应。当我在服务器 A 发送的响应中写入原始客户端时,我想出的一种方法会导致 IllegalArgumentException(我检查了 ChannelBuffer 的内容,并且代理服务器返回了正确的文本)。我在下面粘贴了异常的部分堆栈跟踪。欢迎其他评论,因为除了明显缺乏对服务器 B 的通道锁定之外,我可能还会犯其他错误:
public class NettyExample {
private static Channel channel;
private static Map<Channel, Channel> proxyToClient = new ConcurrentHashMap<Channel, Channel>();
public static void main(String[] args) throws Exception {
ChannelFactory clientFactory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
final ClientBootstrap cb = new ClientBootstrap(clientFactory);
cb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestEncoder(),
new HttpResponseDecoder(),
new ResponseHandler());
}
});
ChannelFuture cf = cb.connect(new InetSocketAddress("localhost", 18080));
channel = cf.awaitUninterruptibly().getChannel();
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap sb = new ServerBootstrap(factory);
sb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestDecoder(),
new RequestHandler());
}
});
sb.setOption("child.tcpNoDelay", true);
sb.setOption("child.keepAlive", true);
sb.bind(new InetSocketAddress(2080));
}
private static class ResponseHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) {
final HttpResponse proxyResponse = (HttpResponse) e.getMessage();
Channel clientChannel = proxyToClient.get(e.getChannel());
HttpResponse clientResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
clientResponse.setContent(proxyResponse.getContent());
clientChannel.write(clientResponse).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
Channel ch = future.getChannel();
ch.close();
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
private static class RequestHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) {
final HttpRequest request = (HttpRequest) e.getMessage();
System.out.println("calling client channel");
proxyToClient.put(channel, e.getChannel());
channel.write(request);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
}
这个中继调用似乎有效,直到调用 clientChannel.write(clientResponse)。在那里,会生成以下异常:
java.lang.IllegalArgumentException: unsupported message type: class org.jboss.netty.handler.codec.http.DefaultHttpResponse
at org.jboss.netty.channel.socket.nio.SocketSendBufferPool.acquire(SocketSendBufferPool.java:53)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.write0(AbstractNioWorker.java:468)
at org.jboss.netty.channel.socket.nio.AbstractNioWorker.writeFromTaskLoop(AbstractNioWorker.java:432)