Netty服务如何使用Nginx代理转发请求并获得原始IP

Nginx配置

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需要调整代码。

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影响到。

你可能感兴趣的:(网络协议,java,nginx,tcp/ip,netty,代理)