IP 归属用 Ip2region 就够了

文章介绍了Ip2region库的功能和特性,包括它的高速查询响应和对多种编程语言的支持。通过示例展示了如何在Java中使用Ip2region进行IP地址到地区信息的转换,以及如何处理HTTP请求中的IP地址。此外,还提供了依赖管理和查询IP地址的代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


抖音、微博、小红书等各平台相继上线" 网络用户IP地址显示功能", 境外显示 国家境内显示到 省市,且该功能无法关闭,IP地址为强制显示。无疑更加有效的约束键盘侠的言行举止,还原一个干净的网络环境!

作为技术人来说其实这个功能so easy,下面借助Ip2region来实现

Ip2region 简介

是什么

ip2region v2.0 是一个离线IP地址定位库和IP定位数据管理框架,10微秒级别的查询效率,提供了众多主流编程语言的xdb数据生成和查询客户端实现。

特性

  • 标准化的数据格式

每个ip数据段的region信息都固定了格式:国家|区域|省份|城市|ISP,只有中国的数据绝大部分精确到了城市,其他国家部分数据只能定位到国家,后前的选项全部是0。

  • 数据去重和压缩

xdb格式生成程序会自动去重和压缩部分数据,默认的全部IP数据,生成的 ip2region.xdb数据库是11MiB,随着数据的详细度增加数据库的大小也慢慢增大。

  • 极速查询响应

即使是完全基于xdb文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:

  1. vIndex 索引缓存:使用固定的512KiB的内存空间缓存vector index 数据,减少一次IO磁盘操作,保持平均查询效率稳定在10-20微秒之间。
  2. xdb 整个文件缓存:将整个xdb文件全部加载到内存,内存占用等同于xdb 文件大小,无磁盘 IO 操作,保持微秒级别的查询效率。

注:下文实操以缓存 xdb 整个文件为例

支持的编程语言

binding描述开发状态binary查询耗时b-tree查询耗时memory查询耗时
cANSC c binding已完成0.0x毫秒0.0x毫秒0.00x毫秒
c#c# binding已完成0.x毫秒0.x毫秒0.1x毫秒
golanggolang binding已完成0.x毫秒0.x毫秒0.1x毫秒
javajava binding已完成0.x毫秒0.x毫秒0.1x毫秒
lualua实现的binding已完成0.x毫秒0.x毫秒0.x毫秒
lua_clua的c扩展已完成0.0x毫秒0.0x毫秒0.00x毫秒
nginxnginx的c扩展已完成0.0x毫秒0.0x毫秒0.00x毫秒
nodejsnodejs已完成0.x毫秒0.x毫秒0.1x毫秒
phpphp实现的binding已完成0.x毫秒0.1x毫秒0.1x毫秒
php5_extphp5的c扩展已完成0.0x毫秒0.0x毫秒0.00x毫秒
php7_extphp7的c扩展已完成0.0毫秒0.0x毫秒0.00x毫秒
pythonpython bindng已完成0.x毫秒0.x毫秒0.x毫秒
rustrust binding已完成0.x毫秒0.x毫秒0.x毫秒

案例实操

依赖

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.7.0</version>
</dependency>

获取IP

根据Request请求,从请求头中获取IP地址

package cn.goitman.utils;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author Nicky
 * @version 1.0
 * @className IpUtil
 * @blog goitman.cn | blog.csdn.net/minkeyto
 * @description 解析IP地址工具
 * @date 2023/3/23 16:45
 */
public class IpUtil {

    private static Logger log = LoggerFactory.getLogger(IpUtil.class);

    private static final String UNKNOWN = "unknown";

    public static String getIpAddress(HttpServletRequest request) {
        String ip = null;
        try {
            // k8s将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。
            ip = request.getHeader("X-Original-Forwarded-For");
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Forwarded-For");
            }
            // 通过nginx获取ip
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("x-forwarded-for");
            }
            // 通过Apache代理获取ip
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            // 通过WebLogic代理获取ip
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            // 通过负载均衡获取IP地址(HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR)
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            // 通过Nginx获取ip(Nginx中的另一个变量,内容就是请求中X-Forwarded-For的信息)
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            //兼容集群获取ip
            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                // 客户端和服务器为同一台机器时,获取的地址为IPV6格式:"0:0:0:0:0:0:0:1"
                if ("127.0.0.1".equalsIgnoreCase(ip) || "0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)) {
                    //根据网卡取本机配置的IP
                    InetAddress iNet = null;
                    try {
                        iNet = InetAddress.getLocalHost();
                        ip = iNet.getHostAddress();
                    } catch (UnknownHostException e) {
                        log.error("根据网卡获取IP地址异常: ", e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("获取IP地址异常 ", e);
        }
        //使用代理,则获取第一个IP地址
        if (!StringUtils.isEmpty(ip) && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        return ip;
    }
}

输入流转化

package cn.goitman.utils;

import java.io.*;

/**
 * @author Nicky
 * @version 1.0
 * @className FileUtil
 * @blog goitman.cn | blog.csdn.net/minkeyto
 * @description 输入流工具
 * @date 2023/3/23 17:22
 */
public class InputStreamUtil {

    /**
    * 将输入流转化为字节数组
    */
    public static byte[] inputStreamToByteArray(InputStream inputStream) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int num;
            while ((num = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, num);
            }
            byteArrayOutputStream.flush();
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

解析IP

下载ip2region仓库中的ip2region.xdb文件,然后放到resource目录下

ip2region.xdb文件路径:https://github.com/lionsoul2014/ip2region/tree/master/data

package cn.goitman.service;

import cn.goitman.utils.InputStreamUtil;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * @author Nicky
 * @version 1.0
 * @className SearcherFile
 * @blog goitman.cn | blog.csdn.net/minkeyto
 * @description 解析ip地址属性
 * @date 2023/3/23 17:21
 */
@Service
public class SearcherService {
    public String getRegion(String ip) {
        // jar包也能获取ip2region.xdb文件
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("ip2region.xdb");
        byte[] bytes = InputStreamUtil.inputStreamToByteArray(in);

        try {
            Searcher searcher = Searcher.newWithBuffer(bytes);

            long sTime = System.nanoTime();
            // 中国|0|上海|上海市|联通;美国|0|犹他|盐湖城|0
            String regionInfo = searcher.search(ip);
            String region = getCityInfo(regionInfo);
            long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
            System.out.printf("{IP属地 : %s, ip: %s, 耗时: %d 纳秒}\n", region, ip, cost);
            return region;
        } catch (Exception e) {
            System.out.printf("IP地址异常 (%s) : %s\n", ip, e);
            return null;
        }
    }

    /**
     * 解析城市信息,国内显示城市名,国外显示国家名
     */
    private String getCityInfo(String regionInfo) {
        if (!StringUtils.isEmpty(regionInfo)) {
            String[] cityArr = regionInfo.replace("|0", "").replace("0|", "").split("\\|");
            if (cityArr.length > 0) {
                if ("内网ip".equalsIgnoreCase(cityArr[0])) {
                    return "内网IP";
                }
                if ("中国".equals(cityArr[0])) {
                    return cityArr[1];
                }
                return cityArr[0];
            }
        }
        return "未知IP";
    }
}

测试

没什么蹊跷,就是这么简单,下班…

源码:https://github.com/wangdaicong/spring-boot-project/tree/master/ip2region-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

靈熙雲

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

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

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

打赏作者

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

抵扣说明:

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

余额充值