Java使用ip2region解析ip获取地区位置


一、Ip2region 是什么

Ip2region 一个 github 的开源项目,即 Ip2region 开源项目。
项目地址:https://github.com/lionsoul2014/ip2region

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

ip2region是完全基于 xdb 文件的查询,单次查询响应时间在十微秒级别

已经有的客户端:Java、C#、php、C、Python、Node.js、PHP 拓展(PHP 5 和 PHP 7)等;

二、ip2region xdb 生成实现

如果不想生成可以直接使用github下载
https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb

下载Ip2region项目

1.编译安装

通过 maven 来编译可运行 jar 程序:

# cd 到 maker/java 根目录
mvn clean compile package

然会会在当前目录的 target 目录下得到一个 ip2region-maker-{version}.jar 的打包文件。

2.数据生成

通过 java -jar ip2region-maker-{version}.jar 来生成 ip2region.xdb 二进制文件:

java git:(java_xdb_maker)java -jar ./target/ip2region-maker-1.0.0.jar 
ip2region xdb maker
java -jar ip2region-maker-{version}.jar [command options]
options:
 --src string    source ip text file path
 --dst string    destination binary xdb file path

例如,通过默认的 data/ip.merge.txt 原数据,在当前目录生成一个 ip2region.xdb 二进制文件:

java git:(java_xdb_maker)java -jar ./target/ip2region-maker-1.0.0.jar --src=../../data/ip.merge.txt --dst=./ip2region.xdb
# 会看到一堆输出,最终会看到如下输出表示运行成功
...
2022-07-15 20:21:29 INFO  org.lionsoul.ip2region.xdb.Maker try to write the vector index block ...
2022-07-15 20:21:29 INFO  org.lionsoul.ip2region.xdb.Maker try to write the segment index ptr ...
2022-07-15 20:21:29 INFO  org.lionsoul.ip2region.xdb.Maker write done, dataBlocks: 13804, indexBlocks: (683591, 720221), indexPtr: (982904, 11065984)
2022-07-15 20:21:29 INFO  org.lionsoul.ip2region.MakerTest Done, elapsed: 50 s

三、Java使用ip2region

1.引入依赖

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

在Maven里面添加依赖,防止把这个xdb文件编译:

<plugin>
  <artifactId>maven-resources-plugin</artifactId>
   <configuration>
    <nonFilteredFileExtensions>
         <nonFilteredFileExtension>xdb</nonFilteredFileExtension>
       </nonFilteredFileExtensions>
     </configuration>
</plugin>

2.完全基于文件的查询

import org.lionsoul.ip2region.xdb.Searcher;
import java.io.*;
import java.util.concurrent.TimeUnit;

public class SearcherTest {
    public static void main(String[] args) {
        // 1、创建 searcher 对象
        String dbPath = "ip2region.xdb file path";
        Searcher searcher = null;
        try {
            searcher = Searcher.newWithFileOnly(dbPath);
        } catch (IOException e) {
            System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e);
            return;
        }

        // 2、查询
        try {
            String ip = "1.2.3.4";
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
        } catch (Exception e) {
            System.out.printf("failed to search(%s): %s\n", ip, e);
        }

        // 3、关闭资源
        searcher.close();
        
        // 备注:并发使用,每个线程需要创建一个独立的 searcher 对象单独使用。
    }
}

3.缓存 VectorIndex 索引

我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。

import org.lionsoul.ip2region.xdb.Searcher;
import java.io.*;
import java.util.concurrent.TimeUnit;

public class SearcherTest {
    public static void main(String[] args) {
        String dbPath = "ip2region.xdb file path";

        // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。
        byte[] vIndex;
        try {
            vIndex = Searcher.loadVectorIndexFromFile(dbPath);
        } catch (Exception e) {
            System.out.printf("failed to load vector index from `%s`: %s\n", dbPath, e);
            return;
        }

        // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
        Searcher searcher;
        try {
            searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
        } catch (Exception e) {
            System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e);
            return;
        }

        // 3、查询
        try {
            String ip = "1.2.3.4";
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
        } catch (Exception e) {
            System.out.printf("failed to search(%s): %s\n", ip, e);
        }
        
        // 4、关闭资源
        searcher.close();

        // 备注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。
    }
}

4.缓存整个 xdb 数据

我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。

import org.lionsoul.ip2region.xdb.Searcher;
import java.io.*;
import java.util.concurrent.TimeUnit;

public class SearcherTest {
    public static void main(String[] args) {
        String dbPath = "ip2region.xdb file path";

        // 1、从 dbPath 加载整个 xdb 到内存。
        byte[] cBuff;
        try {
            cBuff = Searcher.loadContentFromFile(dbPath);
        } catch (Exception e) {
            System.out.printf("failed to load content from `%s`: %s\n", dbPath, e);
            return;
        }

        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
        Searcher searcher;
        try {
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            System.out.printf("failed to create content cached searcher: %s\n", e);
            return;
        }

        // 3、查询
        try {
            String ip = "1.2.3.4";
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
        } catch (Exception e) {
            System.out.printf("failed to search(%s): %s\n", ip, e);
        }
        
        // 4、关闭资源 - 该 searcher 对象可以安全用于并发,等整个服务关闭的时候再关闭 searcher
        // searcher.close();

        // 备注:并发使用,用整个 xdb 数据缓存创建的查询对象可以安全的用于并发,也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
    }
}

5.将ip2region.xdb放在resource


import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import java.io.*;

@Slf4j
public class IpdbUtils {
	private static Searcher searcher;

	/**
	 *   
	 *   解决打包jar后找不到 ip2region.db 的问题
	 */
	public static String getIpAddress(String ip){
		if ("127.0.0.1".equals(ip) || ip.startsWith("192.168")) {
			return "局域网";
		}
		if (searcher == null) {
			try {
				ClassPathResource resource = new ClassPathResource("ipdb/ip2region.xdb");
				InputStream inputStream = resource.getInputStream();
				byte[] bytes = IoUtil.readBytes(inputStream);
				searcher = Searcher.newWithBuffer(bytes);
			} catch (Exception e) {
				log.error("failed to create content cached searcher: ", e);
				return null;
			}
		}
		// 3、查询
		String region=null;
		try {
			region = searcher.search(ip);
		} catch (Exception e) {
			throw new RuntimeException("获取IP地址异常");
		}
		return region;
	}

}

### 如何在PHP中使用ip2region库实现IP地址区域定位 #### 安装ip2region PHP扩展 为了在PHP环境中使用ip2region,需先安装对应的PHP扩展。可以通过Composer轻松完成此操作: ```bash composer require ip2location/ip2location-php ``` 不过需要注意的是,上述命令针对的是`ip2location`包,而官方推荐的方式是直接通过Git克隆仓库并手动集成或利用Composer安装由开发者维护的特定版本。 对于ip2region而言,更合适的做法可能是直接下载发布版或是通过Composer拉取最新稳定版: ```bash composer require lionsoul/ip2region ``` 这一步骤确保了获得专门适配于PHP环境下的ip2region工具[^5]。 #### 加载数据库文件 ip2region提供了一个轻量级的数据文件`ip2region.db`用于存储地理位置映射关系。该文件体积小巧却能保持极高的查询精度,在实际部署时应将其放置在一个易于访问的位置,并通过代码加载到内存中以便快速检索。 ```php <?php require 'vendor/autoload.php'; use Ip2Region\Ip2Region; // 初始化搜索器实例 $ searcher = new Ip2Region('path/to/ip2region.db'); ?> ``` 这段代码展示了如何创建一个新的`Ip2Region`对象并将指定路径中的`.db`文件作为参数传递给构造函数以初始化搜索引擎[^1]。 #### 执行IP地址查询 一旦完成了前期准备工作,就可以调用相应方法来进行具体的IP地址解析工作了。通常情况下会采用如下方式执行查询操作: ```php <?php $result = $searcher->memorySearch($ipAddress); echo "The location of IP {$ipAddress} is: ".json_encode($result); // 或者使用btree算法 $btreeResult = $searcher->btreeSearch($ipAddress); echo "B-tree search result for IP {$ipAddress}: ".json_encode($btreeResult); ?> ``` 这里分别演示了两种不同的查询模式——基于全内存(`memorySearch`)以及基于B树索引(`btreeSearch`)的方法。前者速度更快但由于需要占用更多RAM资源所以适合小型应用;后者则平衡了性能与资源消耗之间的关系[^3]。 #### 查询结果解释 每次成功发起请求后都会返回一个包含匹配信息的结果集。这些信息可能包括国家、省份、城市等多个维度的具体描述。具体结构取决于所使用的数据表设计及其更新情况。 例如,当对某个中国境内的公共IPv4地址进行分析时可能会得到类似于这样的反馈:“中国|广东省|深圳市”。不同分隔符代表各级行政区划单位间的层次关系[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值