乐趣区

关于java:新来了个技术总监居然要我做一个-IP-属地功能不服就干

作者:ThinkingKeep\
链接:https://juejin.cn/post/711895…

仔细的敌人应该会发现,最近,继新浪微博之后,头条、腾讯、抖音、知乎、快手、小红书等各大平台陆陆续续都上线了“网络用户 IP 地址显示性能”,境外用户显示的是国家,国内的用户显示的省份,而且此项显示无奈敞开,归属地强制显示。

作为技术人,那!这个性能要怎么实现呢?

上面,我就来讲讲,Java 中是如何获取 IP 属地的,次要分为以下几步:

  • 通过 HttpServletRequest 对象,获取用户的 IP 地址
  • 通过 IP 地址,获取对应的省份、城市

首先须要写一个 IP 获取的工具类,因为每一次用户的 Request 申请,都会携带上申请的 IP 地址放到申请头中

通过此办法,从申请 Header 中获取到用户的 IP 地址

目前自己在做的我的项目中,也有获取 IP 地址归属地省份、城市的需要,用的是:淘宝 IP 库

地址:ip.taobao.com/

原来的申请源码如下:

能够看到日志 log 文件中,大量的 the request over max qps for user 问题


上面,给大家介绍下之前在 Github 冲浪时发现的明天的配角:

目前最新已更新到了 v2.0 版本,ip2region v2.0 是一个离线 IP 地址定位库和 IP 定位数据管理框架,10 微秒级别的查问效率,准提供了泛滥支流编程语言的 xdb 数据生成和查问客户端实现。

99.9% 准确率:

数据聚合了一些出名 ip 到地名查问提供商的数据,这些是他们官网的的准确率,经测试着实比经典的纯洁 IP 定位精确一些。

多查问客户端的反对

曾经集成的客户端有:java、C#、php、c、python、nodejs、php 扩大 (php5 和 php7)、golang、rust、lua、lua_c, nginx。

binding 形容 开发状态 binary 查问耗时 b-tree 查问耗时 memory 查问耗时
c ANSC c binding 已实现 0.0x 毫秒 0.0x 毫秒 0.00x 毫秒
c# c# binding 已实现 0.x 毫秒 0.x 毫秒 0.1x 毫秒
golang golang binding 已实现 0.x 毫秒 0.x 毫秒 0.1x 毫秒
java java binding 已实现 0.x 毫秒 0.x 毫秒 0.1x 毫秒
lua lua 实现的 binding 已实现 0.x 毫秒 0.x 毫秒 0.x 毫秒
lua_c lua 的 c 扩大 已实现 0.0x 毫秒 0.0x 毫秒 0.00x 毫秒
nginx nginx 的 c 扩大 已实现 0.0x 毫秒 0.0x 毫秒 0.00x 毫秒
nodejs nodejs 已实现 0.x 毫秒 0.x 毫秒 0.1x 毫秒
php php 实现的 binding 已实现 0.x 毫秒 0.1x 毫秒 0.1x 毫秒
php5_ext php5 的 c 扩大 已实现 0.0x 毫秒 0.0x 毫秒 0.00x 毫秒
php7_ext php7 的 c 扩大 已实现 0.0 毫秒 0.0x 毫秒 0.00x 毫秒
python python bindng 已实现 0.x 毫秒 0.x 毫秒 0.x 毫秒
rust rust binding 已实现 0.x 毫秒 0.x 毫秒 0.x 毫秒

Ip2region V2.0 个性

1、标准化的数据格式

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

2、数据去重和压缩

xdb 格局生成程序会主动去重和压缩局部数据,默认的全副 IP 数据,生成的 ip2region.xdb 数据库是 11MiB,随着数据的具体度减少数据库的大小也缓缓增大。

3、极速查问响应

即便是齐全基于 xdb 文件的查问,单次查问响应工夫在十微秒级别,可通过如下两种形式开启内存减速查问:

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

4、极速查问响应

v2.0 格局的 xdb 反对亿级别的 IP 数据段行数,region 信息也能够齐全自定义,例如:你能够在 region 中追加特定业务需要的数据,例如:GPS 信息 / 国内对立地区信息编码 / 邮编等。也就是你齐全能够应用 ip2region 来治理你本人的 IP 定位数据。

ip2region xdb java 查问客户端实现

  • 应用形式

引入 maven 仓库:

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.6.4</version>
</dependency>
  • 齐全基于文件的查问
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 对象独自应用。}
}
  • 缓存 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);
        }

        // 备注:每个线程须要独自创立一个独立的 Searcher 对象,然而都共享全局的制度 vIndex 缓存。}
}
  • 缓存整个 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);
        }

        // 备注:并发应用,用整个 xdb 数据缓存创立的查问对象能够平安的用于并发,也就是你能够把这个 searcher 对象做成全局对象去跨线程拜访。}
}

IDEA 中做个测试

齐全基于文件的查问

ip 属地国内的话,会展现省份,国外的话,只会展现国家。能够通过如下图这个办法进行进一步封装,失去获取 IP 属地的信息。


上面是官网给出的命令运行 jar 形式给出的测试 demo,能够了解下

编译测试程序

通过 maven 来编译测试程序。

# cd 到 java binding 的根目录
cd binding/java/
mvn compile package

而后会在当前目录的 target 目录下失去一个 ip2region-{version}.jar 的打包文件。

查问测试

能够通过 java -jar ip2region-{version}.jar search 命令来测试查问:

➜  java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar search
java -jar ip2region-{version}.jar search [command options]
options:
 --db string              ip2region binary xdb file path
 --cache-policy string    cache policy: file/vectorIndex/content

例如:应用默认的 data/ip2region.xdb 文件进行查问测试:

➜  java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar search --db=../../data/ip2region.xdb
ip2region xdb searcher test program, cachePolicy: vectorIndex
type 'quit' to exit
ip2region>> 1.2.3.4
{region: 美国 |0| 华盛顿 |0| 谷歌, ioCount: 7, took: 82 μs}
ip2region>>

输出 ip 即可进行查问测试,也能够别离设置 cache-policy 为 file/vectorIndex/content 来测试三种不同缓存实现的查问成果。

bench 测试

能够通过 java -jar ip2region-{version}.jar bench 命令来进行 bench 测试,一方面确保 xdb 文件没有谬误,一方面能够评估查问性能:

➜  java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar bench
java -jar ip2region-{version}.jar bench [command options]
options:
 --db string              ip2region binary xdb file path
 --src string             source ip text file path
 --cache-policy string    cache policy: file/vectorIndex/content

例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 文件进行 bench 测试:

➜  java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar bench --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt
Bench finished, {cachePolicy: vectorIndex, total: 3417955, took: 8s, cost: 2 μs/op}

能够通过别离设置 cache-policy 为 file/vectorIndex/content 来测试三种不同缓存实现的成果。@Note: 留神 bench 应用的 src 文件要是生成对应 xdb 文件雷同的源文件。

到这里获取用户 IP 属地曾经实现啦,这篇文章介绍的 v2.0 版本,有趣味的小伙伴能够登录上门的 github 地址理解下 v1.0 版本

如若感觉有用,欢送珍藏 + 点赞,如遇到什么问题,欢送留言探讨

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版