前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 实战系列·Netty 获取客户端 IP

Java 实战系列·Netty 获取客户端 IP

作者头像
数媒派
发布2022-12-01 15:01:44
4.7K0
发布2022-12-01 15:01:44
举报
文章被收录于专栏:产品优化

Netty 获取客户端 IP

近期在后端摸鱼无聊,索性找个练手的项目,最终决定摸一个基于 netty 实现的 WEB IM(在线聊天项目)。项目不大,技术力也不高,期间依旧也踩了不少坑,毕竟咱还是太菜力 😹。

其中一个大坑就是获取 IM 中当前在线用户连接的 IP,这个看起来简单的功能咱着实折腾了好久。

Sping 中获取客户端 IP 很简单,前面咱也写文章介绍过 Spring 获取请求 IP 地址。不过从 netty 中获取远端连接的 IP 着实让咱有点小捉急,虽然 netty 官方原生提供了获取客户端 IP 的方法,但是如果服务器使用了 nginx 代理转发的话,原生提供的方法获取的却是服务器 IP 而非客户端真实 IP。

Google 查阅了不少资料,很多文章都是使用官方默认 IP 获取方式,与咱情况不符,很好奇他们真是这么用的嘛,不使用 nginx 反代???喵喵喵???

最后在 netty github 官方 issues 找到一个方案 Can’t get websocket IP address through amazon ELB,用各种姿势尝试后摸索出一条解决方案。其实这个方法咱一开始就尝试了,但是食用姿势不对,给自己埋了坑,到后面第二次研究时才摸索出正确的解决方式。

获取 IP

首先新增 IP 处理 handler,之前就是因为 handler 顺序搞错了,食用姿势不对,折腾了很久。

You will need to put the handler in front of your upgrade handler.

代码语言:javascript
复制
public class WebSocketChannelInitializer extends ChannelInitializer<NioSocketChannel> {

    @Override
    protected void initChannel(NioSocketChannel ch) {
        // websocket 基于http协议,所以要有http编解码器
        ch.pipeline().addLast(new HttpServerCodec());
        // 对写大数据流的支持
        ch.pipeline().addLast(new ChunkedWriteHandler());
         // IP处理
        ch.pipeline().addLast(IPHandler.INSTANCE);
        // 对httpMessaHeartBeatHandlerge进行聚合,聚合成FullHttpRequest或FullHttpResponse
        ch.pipeline().addLast(new HttpObjectAggregator(1024 * 64));
        // 其他业务处理 handler...
    }
}

IPHandler 实现:

代码语言:javascript
复制
@Slf4j
@ChannelHandler.Sharable
public class IPHandler extends SimpleChannelInboundHandler<HttpObject> {
    public static final IPHandler INSTANCE = new IPHandler();

    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
        if (msg instanceof HttpRequest) {
            HttpRequest mReq = (HttpRequest) msg;
            // 从请求头获取 nginx 反代设置的客户端真实 IP
            String clientIP = mReq.headers().get("X-Real-IP");
            // 如果为空则使用 netty 默认获取的客户端 IP
            if (clientIP == null) {
                InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
                clientIP = insocket.getAddress().getHostAddress();
            }
            ctx.channel().attr(Attributes.IP).set(clientIP);
        }
        // 传递给下一个 handler
        ctx.fireChannelRead(msg);
    }
}

需要注意 SimpleChannelInboundHandler 的匹配规则,它会判断消息体类型,如果匹配则调用 channelRead0(ctx, msg) 处理消息,不会向下一个 handler 传递,否则的话需要调用 ctx.fireChannelRead(msg) 传递数据给下一个 handler。

Nginx 配置

上述方案还需要 nginx 配合,在 nginx 配置中加上客户端真实 IP proxy_set_header X-Real-IP $remote_addr;

代码语言:javascript
复制
server {
    listen 80;
    server_name moechat.com;

    location / {
        proxy_connect_timeout 300s;
        proxy_send_timeout   300s;
        proxy_read_timeout   300s;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://api:8080;
        proxy_redirect http:// https://;
        client_max_body_size 300M;
    }

    location /chat {
        access_log off;
        proxy_pass http://api:7002;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_connect_timeout 1d;
        proxy_send_timeout 1d;
        proxy_read_timeout 1d;
    }

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Netty 获取客户端 IP
    • 获取 IP
      • Nginx 配置
      相关产品与服务
      大数据
      全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档