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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Netty转发HTTP请求,您需要编写一个自定义的ChannelHandler,并将其添加到您的Netty管道中。以下是一个简单的示例: ```java public class HttpProxyHandler extends ChannelInboundHandlerAdapter { private final String remoteHost; private final int remotePort; public HttpProxyHandler(String remoteHost, int remotePort) { this.remoteHost = remoteHost; this.remotePort = remotePort; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; // 创建远程连接 Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) .channel(NioSocketChannel.class) .handler(new HttpProxyClientInitializer(ctx.channel())); ChannelFuture cf = bootstrap.connect(remoteHost, remotePort); cf.addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { // 发送请求 Channel clientChannel = future.channel(); clientChannel.writeAndFlush(request.retain()); // 将代理服务器和客户端之间的连接搭建起来 ctx.pipeline().remove(HttpProxyHandler.this); clientChannel.pipeline().addLast(new HttpProxyServerHandler(ctx.channel())); } else { ctx.channel().close(); } }); return; } ctx.fireChannelRead(msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.channel().close(); } } ``` 在上面的例子中,我们创建了一个名为HttpProxyHandler的自定义ChannelHandler。当接收到HTTP请求时,它将建立一个到远程服务器的连接,并将请求转发到该服务器。一旦连接建立成功,它将从管道中删除自己,并添加一个新的ChannelHandler来处理代理服务器和客户端之间的通信。 请注意,此示例仅处理FullHttpRequest,您可能需要根据您的特定用例进行更改。 最后,您需要将此处理程序添加到您的Netty管道中: ```java public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HttpProxyHandler("localhost", 8080)); } }); ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } ``` 在这个例子中,我们创建了一个简单的代理服务器,它将所有传入的HTTP请求转发到远程服务器(在本例中为localhost:8080)。 希望这个例子能够帮助您开始使用Netty进行HTTP请求转发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不识君的荒漠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值