Nginx启用stream模块,示例如下:
stream {
upstream netty{
server remote:8080;
}
server {
listen 8080;
proxy_pass netty;
proxy_protocol on;
}
}
示例,代理远端8080的netty服务。
注意,获得原始客户端的IP关键配置在于:proxy_protocol on;
这一行配置。如果不配置,在netty服务端是无法获得原始客户端ip,但是配置上之后,netty需要调整代码。
代理http协议的时候,可以通过增加X-Forwarded-For请求头传递。
然而TCP采用另一种方式,每次在建立连接的时候发送一个非常小的报告头信息,提供这些数据,注意也只发送一次。
我们在用Netty的时候,是需要提供相应的编解码器的,因此对于这个代理头Netty内置的有相应的Decoder.
类路径:io.netty.handler.codec.haproxy.HAProxyMessageDecoder
配置示例:
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
if (enableProxy) {
pipeline.addLast("proxyDecoder", new HAProxyMessageDecoder());
}
pipeline.addLast("frameDecoder", new FrameDecoder());
pipeline.addLast("frameEncoder", new FrameEncoder());
pipeline.addLast("protocolEncoder", new ProtocolEncoder());
pipeline.addLast("protocolDecoder", new ProtocolDecoder());
pipeline.addLast("channelHandler", new ChannelHandler());
}
关键就是:
pipeline.addLast("proxyDecoder", new HAProxyMessageDecoder());
这个配置。
放的位置和顺序如上面的示例即可。当我们第一次获得请求头的时候,需要拿到原始的IP等信息,保存下来:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HAProxyMessage) {
HAProxyMessage proxyMessage = (HAProxyMessage) msg;
// 原始IP
proxyMessage.sourceAddress();
// 原始端口
proxyMessage.sourcePort();
} else {
// 业务处理
}
}
可能有的同学奇怪HAProxyMessageDecoder这个解码器放在最上面,对于正常请求有影响没?
答案是没有影响。
正如前面说的,是每次建立连接的时候发送且只发送一次,我们可以看下,HAProxyMessageDecoder的decode逻辑:
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// determine the specification version
if (version == -1) {
if ((version = findVersion(in)) == -1) {
return;
}
}
ByteBuf decoded;
if (version == 1) {
decoded = decodeLine(ctx, in);
} else {
decoded = decodeStruct(ctx, in);
}
if (decoded != null) {
// 注意这里,如果decode成功,会设置finished为true
finished = true;
try {
if (version == 1) {
out.add(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII)));
} else {
out.add(HAProxyMessage.decodeHeader(decoded));
}
} catch (HAProxyProtocolException e) {
fail(ctx, null, e);
}
}
}
留意注释里强调的:finished变量,接着往下看:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
if (finished) {
// 后面都移除了
ctx.pipeline().remove(this);
}
}
解码成功后,后面就将它移除了,所以不用担心后面读取正常请求数据的时候会被这个decoder影响到。