我们在一些短视频平台上可以看到,视频作者或评论区可以显示IP地址,这其实就是根据IP获取到的我们可以通过一些在线网站就可以看到我们当前的公网IP和IP定位,最近有个需求也需要通过请求获取客户端的IP和IP的定位,于是通过一系列的百度,最终选择使用Ip2region这个工具库来进行定位
Ip2region简介
Ip2region是一个开源工具库,是一种用于IP地址定位的工具。 它是基于B树数据结构的数据库,可用于将IP地址转换为国家、省、市、区县等信息。 使用Ip2region工具,您可以轻松地查找某个IP地址所在的区域信息,从而帮助您进行定位和分析。
目前Ip2region有1.0和2.0两个版本,p2region 1.0和Ip2region 2.0之间的主要区别在于数据的更新频率和精度。 Ip2region 2.0在原有版本的基础上增加了更多的数据,例如更详细的地图信息和更准确的IP地址定位。 Ip2region 2.0的数据更新频率也更高,这意味着它将提供更精确的信息。此外, Ip2region 2.0还提供了Java、Python和PHP等语言的版本,这使得它更加适用于不同类型的应用程序。
GitHub地址(CSDN镜像地址,非原地址):https://gitcode.net/mirrors/lionsoul2014/ip2region
Ip2region使用
前面提到Ip2region分为1.0和2.0两个版本,此处我是用的是2.0版本,如需1.0版本此文章并不适用,可以参考如下文章
(153条消息) Java根据ip地址获取归属地_java通过ip地址获取地区_java技术媛的博客-CSDN博客
Ip2region2.0版本引入依赖
2.7.0是当前最新版本,使用Ip2region2.0引入2.6.0版本及以上应该是都可以的
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
下载ip2region.xdb文件
下载链接;https://gitcode.net/mirrors/lionsoul2014/ip2region/-/blob/master/data/ip2region.xdb
如果链接失效:进入上文github地址然后选在data文件夹,里面就有ip2region.xdb文件
下载后将其放在resources文件夹下,注意在pom文件里添加排除配置,防止maven将文件编译
<plugin>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>xdb</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
Searcher介绍
ip2region核心类就是Searcher,在2.0的版本中它有三种创建方式,三种方式使用方式不太一样,接下来逐一介绍
newWithFileOnly(String dbPath)
此种创建方式,只有一个参数,顾名思义,我们可以知道入参其实就是ip2region.xdb文件的路径,此种创建方式是最基础的创建方式,不支持并发,每次创建都需要进行一次IO,读取本地ip2region.xdb文件
newWithVectorIndex(String dbPath, byte[] vectorIndex)
此种创建方式多出来一个vectorIndex参数,它是一个字节数组我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。此种方式也不支持并发,每个线程需要重新new一个Searcher对象
newWithBuffer(byte[] cBuff)
此种创建方式只有一个cBuff参数,它可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询无IO压力,而且仅占用11M内存,最重要的是它支持并发,全局只需要创建一次即可,也是查询效率最高的一种方法,下面我自己封装了一个工具类,就是采用的这种方式
工具类代码
/**
* ip解析工具
*
* @author zzt
* @version v1.0.0
* @date 2023/6/9 9:26
*/
@Slf4j
public class IpParseUtil {
/**
* 将整个xdb文件加载到内存中(11M左右),此种创建方式支持多线程,因此只需要加载一次
*/
private final static Searcher SEARCHER;
static {
try {
ClassPathResource resource = new ClassPathResource("ip2region.xdb");
//获取真实文件路径
String path = resource.getURL().getPath();
byte[] cBuff = Searcher.loadContentFromFile(path);
SEARCHER = Searcher.newWithBuffer(cBuff);
log.info("加载了ip2region.xdb文件,Searcher初始化完成!");
} catch (Exception e) {
log.error("初始化ip2region.xdb文件失败,报错信息:[{}]", e.getMessage(), e);
throw new RuntimeException("系统异常!");
}
}
/**
* 解析ip地址
*
* @param ipStr 字符串类型ip 例:192.168.0.1
* @return 返回结果形式(国家|区域|省份|城市|ISP) 例 [中国, 0, 河北省, 衡水市, 电信]
*/
public static List<String> parse(@NotBlank String ipStr) {
return parse(ipStr, null);
}
/**
* 自定义解析ip地址
*
* @param ipStr ip 字符串类型ip 例:1970753539(经过转换后的)
* @param index 想要获取的区间 例如:只想获取 省,市 index = [2,3]
* @return 返回结果例 [北京,北京市]
*/
public static List<String> parse(@NotBlank String ipStr, int[] index) {
try {
long ip = Searcher.checkIP(ipStr);
return parse(ip, index);
} catch (Exception e) {
log.error("ip解析为long错误,ipStr:[{}],错误信息:[{}]", ipStr, e.getMessage(), e);
throw new RuntimeException("系统异常!");
}
}
/**
* 自定义解析ip地址
*
* @param ip ip Long类型ip 例:192.168.0.1
* @param index 想要获取的区间 例如:只想获取 省,市 index = [2,3]
* @return 返回结果例 [河北省, 衡水市]
*/
public static List<String> parse(@NotNull Long ip, int[] index) {
//获取xdb文件资源
List<String> regionList = new ArrayList<>();
try {
String region = SEARCHER.search(ip);
String[] split = region.split("\\|");
if (index == null) {
regionList = Arrays.asList(split);
} else {
for (int i : index) {
regionList.add(split[i]);
}
}
//关闭资源
SEARCHER.close();
} catch (Exception e) {
log.error("根据ip解析地址失败,ip:[{}],index[{}],报错信息:[{}]", ip, index, e.getMessage(), e);
throw new RuntimeException("系统异常!");
}
return regionList;
}
}
简单测试
public class IPTest {
public static void main(String[] args) {
//只获取省,市
int[] index = {2, 3};
List<String> parse = IpParseUtil.parse("222.223.145.133", index);
System.out.println(parse);
}
}