最简路径是:maven引入ip2region 2.7.0依赖,下载v2格式ip2region.db放resources下,单例初始化dbsearcher并传入文件路径,查ip前清洗ipv4字符串,用btreesearch获取region对象解析归属地。

用 ip2region 读取离线 DB 文件最简路径
Java 里想不联网查 IP 归属地,ip2region 是目前最轻、最稳、更新勤的离线方案。它用的是二分搜索 + 内存映射(MMap),查一次通常在微秒级,比自己解析纯真库快一个数量级。
实操上,别去 clone 官方仓库手动编译 —— 直接用 Maven 引入现成的封装包:
<dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>2.7.0</version> </dependency>
然后下载最新 ip2region.db 文件(GitHub releases 页面找),放到 resources/ 下或任意可读路径。注意:文件必须是 v2 格式(后缀带 .db,不是 .txt 或旧版 .dat)。
-
DbSearcher初始化只做一次,建议单例或 Spring Bean 管理,避免重复 mmap 开销 - 构造时传入
new DbConfig()和new File("path/to/ip2region.db"),路径错会直接抛IOException - 别用
MemorySearcher模式——它把整个 DB 加载进堆内存,14MB DB ≈ 占 50MB 堆,GC 压力大且无必要
查 IP 时为什么总返回空或 “未知”
常见不是代码写错,而是输入没过清洗。IP 字符串里混了空格、换行、IPv6 地址、甚至 HTTP 头字段(比如 X-Forwarded-For: 1.2.3.4, 192.168.0.1)都会导致 search 返回空结果。
立即学习“Java免费学习笔记(深入)”;
务必在调用前做三件事:
- 用正则
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}提取首个 IPv4 地址,丢掉逗号后所有内容 - 用
InetAddress.getByName(ipStr).isAnyLocalAddress()过滤掉0.0.0.0、127.0.0.1等无效地址 - 确保字符串不含中文、全角符号、不可见字符(
ipStr.trim().replaceAll("\s+", ""))
示例错误输入:" 114.114.114.114
" → 不 trim 就查不到;"::1" → ip2region 不支持 IPv6,会静默失败
search 返回的字符串结构怎么拆
DbSearcher.search(String ip) 返回的是一个用 | 分隔的字符串,格式固定为:国家|区域|省份|城市|ISP,共 5 段。但实际经常有空段,比如海外 IP 可能是 "美国||加利福尼亚州|圣何塞|Cloudflare"。
别用 split("|") —— 它会按正则解析竖线,得写成 split("\|")。更稳妥的做法是用 DbSearcher.btreeSearch 的 Region 对象(v2.7+ 支持),它直接提供字段:
Region region = searcher.memorySearch(ip); String country = region.getCountry(); String province = region.getProvince(); // 可能为 null 或空串 String city = region.getCity();
- 如果只关心“中国+省份”,优先看
country是否含 “中国”,再取province,别依赖索引 2(有些港澳台地址索引偏移不同) -
city字段可能包含区/县(如 “杭州市西湖区”),如需精确到区,得额外切分,ip2region本身不保证三级行政区粒度 - 返回
null不代表出错,大概率是 IP 不在库中(比如新分配的云厂商段),不是异常要 catch
高并发下 DbSearcher 实例能不能共享
能,而且必须共享。它的核心方法(btreeSearch、binarySearch)全是无状态的,内部只读 mmap 区域,线程安全。创建多个实例反而浪费内存和文件句柄。
但要注意两个边界:
- 不要在多线程里共用同一个
BufferedReader或自定义RandomAccessFile实例 ——DbSearcher自己管理底层 IO,你只管传路径 - Spring Boot 中,把它声明为
@Bean(scope = ConfigurableBeanFactory.SCOPE_SINGLETON),别用prototype,否则每次注入都新建 mmap - 如果部署在容器环境(Docker/K8s),确认
ip2region.db文件被正确挂载且权限为只读,mmap 在只读模式下更稳定
真正容易被忽略的是 DB 文件更新机制:没有热加载。换新 ip2region.db 后,必须重启应用或手动重建 DbSearcher 实例 —— 它不会监听文件变化自动 reload。










