惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Microsoft Azure Blog
Microsoft Azure Blog
WordPress大学
WordPress大学
小众软件
小众软件
Last Week in AI
Last Week in AI
月光博客
月光博客
博客园 - 聂微东
Recent Announcements
Recent Announcements
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
云风的 BLOG
云风的 BLOG
量子位
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
博客园 - 司徒正美
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享

博客园 - VipSoft

FastAPI 全局 HTTP 异常处理器 + 统一响应封装 SpringBoot 心跳日志不记录 access.log Qdrant Linux 安装(非Docker) LangChain — RAG 知识库(实操) LangChain — RAG 构建知识库(理论) LangChain — RAG 构建知识库(实操) Python PyCharm 运行,取不到 .env 文件中的值 Qdrant 安装(Windows) LangChain — RAG 构建知识库 Python 项目简单部署(Linux) MinerU - 将非结构化文档(PDF、图片、Office 文件等)转换为机器可读的 Markdown 和 JSON LangChain 入门 服务端部署-FastAPI LangChain 入门 LangSmith LangChain 入门 实战 - 食谱推荐 LangChain 入门 Memory 会话记忆 LangChain 入门 Tools 工具 LangChain 入门 Tools 工具 LangChain 入门 Prompts 提示词 LangChain 入门 Message 消息 LangChain 入门 Model 的初始化和调用 LangChain 入门 Agent 的基本运行机制 AI 0基础学习,名词解析 LangChain 和 LangGraph AI大模型知识体系 Dify — Workflow - 数据可视化 Dify — 连接MySQL配置 Dify — Chatflow - 数据库智能查询 Dify — Chatflow - 文档知识库 Dify — Agent 智能体 高安全券码、注册码生成
SpringBoot 集成 IP2Region 获取IP地域信息
VipSoft · 2026-03-10 · via 博客园 - VipSoft

在Java生态中,获取IP地域信息主要有以下几种方案:

IP2Region

离线查询,速度快,免费

数据更新需要下载新库

高并发,离线环境

MaxMind GeoIP2

数据准确,功能丰富

商业版收费,需要更新数据库

商业应用,需要精确数据

在线API服务

无需维护数据库,使用简单

依赖网络,有速率限制

低频次查询,简单应用

IP2Region是一个高效的离线IP地域查询库,具有以下特点:

极致性能:微秒级的查询速度,单核可达1000万次/天
零依赖:纯Java实现,无需第三方依赖
离线查询:不依赖网络请求,数据存储在本地
简单易用:API设计简洁,上手快速

一、添加 Maven 依赖

https://www.cnblogs.com/vipsoft/p/18583288

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

该依赖包含了 IP2Region 查询库,使得你能够在 Spring Boot 项目中使用 Searcher 类进行高效的 IP 地址查询。

二、配置数据库文件路径与版本

在 Spring Boot 的 application.properties 或 application.yml 中配置 IP2Region 数据库文件的路径和 IP 版本(IPv4 或 IPv6)。
application.yml

ip2region:
  db-path: ip2region_v4.xdb  # 可根据需要替换为 IPv6 文件路径
  version: IPv4

ip2region_v4.xdbIP2Region 的数据库文件,你需要下载并将其放置在 src/main/resources 目录下,确保 Spring Boot 能够通过 classpath 读取。

三、创建 IP 查询服务


import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.xdb.Version;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;


@Service
public class IpService {

    private Searcher searcher;

    // 从配置文件中加载数据库路径和版本
    @Value("${ip2region.db-path}")
    private String dbPath;

    @Value("${ip2region.version}")
    private String version;

    // 服务启动时初始化 Searcher
    @PostConstruct
    public void init() throws IOException {
        // 根据配置选择 IP 版本
        Version ipVersion = version.equalsIgnoreCase("IPv4") ? Version.IPv4 : Version.IPv6;

        try {
            // 使用 ClassPathResource 加载类路径中的数据库文件
            Resource resource = new ClassPathResource(dbPath);
            if (!resource.exists()) {
                throw new IOException("Unable to find " + dbPath + " in classpath");
            }

            // 获取文件的输入流
            InputStream inputStream = resource.getInputStream();

            // 将 InputStream 保存到临时文件
            Path tempFile = Files.createTempFile("ip2region", ".xdb");
            try (OutputStream out = new FileOutputStream(tempFile.toFile())) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            }

            // 使用临时文件创建 Searcher 对象
            searcher = Searcher.newWithFileOnly(ipVersion, tempFile.toString());

        } catch (IOException e) {
            System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e);
            throw new IOException("Failed to initialize IP2Region searcher", e);
        }
    }

    // 查询 IP 地理位置
    public String getCityInfo(String ip) {
        try {
            long sTime = System.nanoTime();
            String region = searcher.search(ip);  // 查询 IP 地址
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
            return region;
        } catch (Exception e) {
            System.out.printf("failed to search(%s): %s\n", ip, e);
            return null;
        }
    }

    // 在 Spring Boot 中使用 @PreDestroy 优雅地关闭 searcher
    public void close() throws IOException {
        if (searcher != null) {
            searcher.close();
        }
    }
}
  • @PostConstruct:这是 Spring 提供的生命周期注解,表示方法将在 Bean 初始化完成后执行。我们在这里初始化 Searcher 实例,基于配置的数据库文件路径和 IP 版本。
  • getCityInfo:此方法用于查询 IP 地址的地理位置信息。它会返回一个字符串,包含 IP 地址的地理位置(如国家、省份、城市等)。

四、创建控制器提供查询 API

为了让外部可以查询 IP 地址,我们创建一个 REST 控制器类 IpController,通过 HTTP GET 请求提供查询接口。
IpController.java

package com.example.ip2region.controller;

import com.example.ip2region.service.IpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IpController {

    private final IpService ipService;

    @Autowired
    public IpController(IpService ipService) {
        this.ipService = ipService;
    }

    // 查询 IP 地址的地理信息
    @GetMapping("/get-ip-location")
    public String getIpLocation(@RequestParam String ip) {
        return ipService.getCityInfo(ip);
    }
}

解析:

@GetMapping("/get-ip-location"):此注解将 HTTP GET 请求映射到 getIpLocation 方法。通过 @RequestParam 获取 IP 地址参数,并查询该 IP 的地理位置。
该控制器提供了一个查询接口,可以通过 http://localhost:8080/get-ip-location?ip=1.2.3.4 查询 IP 地址 1.2.3.4 的地理位置。

六、优化:使用缓存来加速查询

IP2Region 支持使用缓存来优化查询性能。你可以通过预加载 VectorIndex 或整个 XDB 文件的内容,将其存储在内存中,从而减少文件 IO 操作,提升查询速度。

package com.donglin.service;

import jakarta.annotation.PostConstruct;
import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.xdb.Version;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;

@Service
public class IpService {

    private Searcher searcher;

    @Value("${ip2region.db-path}")
    private String dbPath;

    @Value("${ip2region.version}")
    private String version;

    @PostConstruct
    public void init() throws IOException {
        Version ipVersion = version.equalsIgnoreCase("IPv4") ? Version.IPv4 : Version.IPv6;

        try {
            // ✅ 1. 从 classpath 读取 XDB 文件(无论打包与否都兼容)
            Resource resource = new ClassPathResource(dbPath);
            if (!resource.exists()) {
                throw new IOException("无法在 classpath 中找到文件:" + dbPath);
            }

            // ✅ 2. 先把 XDB 文件写入临时文件(VectorIndex 只能基于文件加载)
            Path tempFile = Files.createTempFile("ip2region", ".xdb");
            try (InputStream is = resource.getInputStream()) {
                Files.copy(is, tempFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
            }

            // ✅ 3. 预加载 VectorIndex(大幅减少 I/O)
            byte[] vIndex;
            try {
                vIndex = Searcher.loadVectorIndexFromFile(tempFile.toString());
                System.out.println("VectorIndex loaded, length = " + vIndex.length);
            } catch (Exception e) {
                throw new IOException("加载 VectorIndex 失败: " + e.getMessage(), e);
            }

            // ✅ 4. 创建使用 VectorIndex 缓存的 Searcher
            searcher = Searcher.newWithVectorIndex(ipVersion, tempFile.toString(), vIndex);
            System.out.println("IP2Region searcher initialized with vector index cache.");

        } catch (IOException e) {
            System.err.printf("failed to create searcher with `%s`: %s%n", dbPath, e);
            throw new IOException("Failed to initialize IP2Region searcher", e);
        }
    }

    // 查询 IP 地理位置
    public String getCityInfo(String ip) {
        try {
            long start = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - start);
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}%n",
                    region, searcher.getIOCount(), cost);
            return region;
        } catch (Exception e) {
            System.err.printf("failed to search(%s): %s%n", ip, e);
            return null;
        }
    }

    public void close() throws IOException {
        if (searcher != null) {
            searcher.close();
        }
    }
}

通过将 ip2region 集成到 Spring Boot 中,我们可以非常高效地查询 IP 地址的地理位置信息。通过预加载缓存(如 VectorIndex 或整个 XDB 文件),我们可以进一步提高查询性能,减少 IO 压力,尤其是在高并发场景下。

image