◆一、开源项目简介
◆二、开源协议
使用Apache-2.0开源协议
◆三、界面展示
◆四、功能概述◆Ip2region特性◆99.9%准确率
◆标准化的数据格式
每条ip数据段都固定了格式:
_城市Id|国家|区域|省份|城市|ISP_
只有中国的数据精确到了城市,其他国家有部分数据只能定位到国家,后前的选项全部是0,已经包含了全部你能查到的大大小小的国家(请忽略前面的城市Id,个人项目需求)。
◆体积小
包含了全部的IP,生成的数据库文件ip2region.db只有几MB,最小的版本只有1.5MB,随着数据的详细度增加数据库的大小也慢慢增大,目前还没超过8MB。
◆查询速度快
全部的查询客户端单次查询都在0.x毫秒级别,内置了三种查询算法
memory算法:整个数据库全部载入内存,单次查询都在0.1x毫秒内,C语言的客户端单次查询在0.00x毫秒级别。
binary算法:基于二分查找,基于ip2region.db文件,不需要载入内存,单次查询在0.x毫秒级别。
b-tree算法:基于btree算法,基于ip2region.db文件,不需要载入内存,单词查询在0.x毫秒级别,比binary算法更快。
任何客户端b-tree都比binary算法快,当然memory算法固然是最快的!
◆五、技术选型◆多查询客户端的支持
已经集成的客户端有: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快速测试
请参考每个binding下的README说明去运行cli测试程序,例如C语言的demo运行如下:
cd binding/c/gcc -g -O2 testSearcher.c ip2region.c./a.out ../../data/ip2region.db
会看到如下cli界面:
initializing B-tree … ———————————- | ip2region test script || Author: chenxin619315@gmail.com || Type ‘quit’ to exit program | ———————————- p2region>> 101.105.35.572163|中国|华南|广东省|深圳市|鹏博士 in 0.02295 millseconds
◆ip2region安装
具体请参考每个binding下的README文档和测试demo,以下是一些可用的快捷安装方式:
全部binding的各个search接口都不是线程安全的实现,不同线程可以通过创建不同的查询对象来使用,并发量很大的情况下,binary和b-tree算法可能会打开文件数过多的错误,请修改内核的最大允许打开文件数(fs.file-max=一个更高的值),或者使用持久化的memory算法。
memorySearch接口,在发布对象前进行一次预查询(本质上是把ip2region.db文件加载到内存),可以安全用于多线程环境。
◆ip2region.db的生成
从1.8版本开始,ip2region开源了ip2region.db生成程序的java实现,提供了ant编译支持,编译后会得到以下提到的dbMaker-{version}.jar,对于需要研究生成程序的或者更改自定义生成配置的请参考${ip2region_root}/maker/java内的java源码。
从ip2region 1.2.2版本开始里面提交了一个dbMaker-{version}.jar的可以执行jar文件,用它来完成这个工作:
确保你安装好了java环境(不玩Java的童鞋就自己谷歌找找拉,临时用一用,几分钟的事情)
cd到${ip2region_root}/maker/java,然后运行如下命令:
java -jar dbMaker-{version}.jar -src 文本数据文件 -region 地域csv文件 [-dst 生成的ip2region.db文件的目录]# 文本数据文件:db文件的原始文本数据文件路径,自带的ip2region.db文件就是/data/ip.merge.txt生成而来的,你可以换成自己的或者更改/data/ip.merge.txt重新生成# 地域csv文件:该文件目的是方便配置ip2region进行数据关系的存储,得到的数据包含一个city_id,这个直接使用/data/origin/global_region.csv文件即可# ip2region.db文件的目录:是可选参数,没有指定的话会在当前目录生成一份./data/ip2region.db文件
获取生成的ip2region.db文件覆盖原来的ip2region.db文件即可
默认的ip2region.db文件生成命令:
cd ${ip2region_root}/java/java -jar dbMaker-1.2.2.jar -src ./data/ip.merge.txt -region ./data/global_region.csv# 会看到一大片的输出◆架构原理
本文将分三个部分:
源数据转变成ip2region db 文件的过程
ip2region 的结构
搜索方法
ip2region 的ip数据来自纯真和淘宝的ip数据库,每次抓取完成之后会生成 ip.merge.txt, 再通过程序根据这个源文件生成ip2region.db 文件。
ip.merge.txt 中每一行对应一条完整记录,每一条记录由ip段和数据组成,格式如下:
0.0.0.0|0.255.255.255|未分配或者内网IP|0|0|0|01.0.0.0|1.0.0.255|澳大利亚|0|0|0|01.0.1.0|1.0.3.255|中国|华东|福建省|福州市|电信1.0.4.0|1.0.7.255|澳大利亚|0|0|0|01.0.8.0|1.0.15.255|中国|华南|广东省|广州市|电信1.0.16.0|1.0.31.255|日本|0|0|0|01.0.32.0|1.0.63.255|中国|华南|广东省|广州市|电信1.0.64.0|1.0.127.255|日本|0|0|0|01.0.128.0|1.0.255.255|泰国|0|0|0|01.1.0.0|1.1.0.255|中国|华东|福建省|福州市|电信
从左到右分别表示:起始ip,结束ip,国家,区域,省份,市,运营商。无数据区域默认为0。
◆2. 如何生成ip2region.db
给定一个ip,如何快速从ip.merge.txt中找到该ip所属记录?最简单的办法就是顺序遍历,当该ip在某条记录起始和结束ip之间时,即命中。
这是低效的做法,如何提高查询性能?用过mysql和其他数据库的的都知道,使用索引。所以ip2region.db使用了内建索引,直接将性能提升到0.0x毫秒级别。
此时的数据库文件中的每一条索引都指向一条对应的数据,也就是说如
|中国|华南|广东省|广州市|电信
这样的数据在文件中被重复存储了很多次,再经过去重优化之后,ip2region.db只有1.5M了,此时把数据库文件全部读取到内存再查找都是非常可行的。
◆(二). ip2region.db 结构
生成的ip2region.db文件包含以下四个部分:
1, SUPER BLOCK2, HEADER INDEX3, DATA4, INDEX
生成 ip2region.db 的时候,首先会在首部预留 8 bytes 的SUPER BLOCK 和 8k 的 HEADER INDEX。
当 INDEX 索引区和 DATA 数据区确定下来之后,再把 INDEX 的起始位置存储到 SUPER BLOCK 的前四个字节,结束位置存储到 SUPER BLOCK 的后四个字节。
再把 INDEX 分成大小为 4K 的索引分区,把每个分区起始位置的索引的起始ip和该索引的位置存入一个 header index block, 组成 HEADER INDEX 区域, 最后写入ip2region.db。
具体功能:
INDEX
索引区域,索引元素为 index block (12 字节), 分成三个部分,起始ip, 结束ip, 数据信息, 每一条 index block 对应 ip.merge.txt 中的一条记录。
每个index block 表示一个ip段的索引。当指定ip 在某个 index block 的起始ip和结束ip中间,即表示命中索引。
SUPER BLOCK
HEADER INDEX
HEADER INDEX 区是对 INDEX 区的二级索引, INDEX总长度除以 4K 就是 HEADER INDEX 的实际索引数。
该区域长度为8k, 由 8 bytes 的 header index block 组成。
DATA
保存的数据,数据格式如下:
2163|中国|华南|广东省|深圳市|鹏博士
分别表示 城市ip,国家,区域,省份,城市,运营商
◆(三). 搜索方法◆binary搜索
二分法就不多介绍了,步骤:
把ip值通过ip2long方法转为长整型
通过 SUPER BLOCK 拿到INDEX的起始位置和结束位置
相减 1得出index block 总数
采用二分法直接求解,比较 index block 和当前ip的大小,即可找到该ip属于的 index block
以php客户端作为例子注释:
<?php fseek($this->dbFileHandler, 0); $superBlock = fread($this->dbFileHandler, 8); // 从文件0位置往后读取8字节,即 super block $this->firstIndexPtr = self::getLong($superBlock, 0); // 获取INDEX起始位置 $this->lastIndexPtr = self::getLong($superBlock, 4); // 获取INDEX结束位置 $this->totalBlocks = ($this->lastIndexPtr-$this->firstIndexPtr)/INDEX_BLOCK_LENGTH 1; // 计算总索引数,即 index block 总数 // 二分法搜索 $l = 0; // 低位 $h = $this->totalBlocks; // 高位 $dataPtr = 0; while ( $l <= $h ) { $m = (($l $h) >> 1); // 中位 $p = $m * INDEX_BLOCK_LENGTH; fseek($this->dbFileHandler, $this->firstIndexPtr $p); // 移动读取位置 $buffer = fread($this->dbFileHandler, INDEX_BLOCK_LENGTH); // 读取 INDEX_BLOCK_LENGTH 个字节 (12 字节), 即读取一个index block $sip = self::getLong($buffer, 0); // 获取开始 ip // 进行比较 if ( $ip < $sip ) { $h = $m – 1; // 比中位index block 开始ip小 } else { $eip = self::getLong($buffer, 4); if ( $ip > $eip ) { $l = $m 1; // 比中位index block 的结束ip小 } else { // 命中数据 $dataPtr = self::getLong($buffer, 8); // getLong 函数将字节的顺序反过来了 break; } } } // 下面这段代码看起来似乎是,第一个字节存储的长度,后三个字节存储的数据位置 // 其实是上文的 getLong 函数在获取数据的时候对字节顺序做了一下反转,具体参考 getLong 函数的代码 // 读取数据 $dataLen = (($dataPtr >> 24) & 0xFF); // 数据长度 $dataPtr = ($dataPtr & 0x00FFFFFF); // 数据位置 return array( ‘city_id’ => self::getLong($this->dbBinStr, $dataPtr), // 获取城市id ‘region’ => substr($this->dbBinStr, $dataPtr 4, $dataLen – 4) // 获取其他数据 );?>
源码请查阅 ip2region php client 的 binarySearch 方法。
◆b-tree 搜索
b-tree 搜索用到了 HEADER INDEX,第一步先在 HEADER INDEX 中搜索,再定位到 INDEX 中的某个 4k index分区搜索。
步骤:
把ip值通过ip2long 转为长整型
使用二分法在 HEADER INDEX 中搜索,比较得到对应的 header index block
header index block 指向 INDEX 中的一个 4K 分区,所以直接把搜索范围降低到 4K
采用二分法在获取到的 4K 分区搜索,得到对应的 index block
具体源码请查阅 ip2region php client 中的 btreeSearch 方法。
https://www.toutiao.com/article/7094257165510197800/?log_from=b932330b01a2c_1651909510853
来都来了,走啥走,留个言呗~
IT大咖说 |关于版权
感谢您对IT大咖说的热心支持!
相关推荐
推荐文章
跟xshell说再见,推荐免费的终端连接器WindTerm
ICLR 2022 | 走向深度图神经网络:基于GNTK的优化视角
不会还有人不懂Stream源码吧?10年架构师带你一次性搞懂
2 万字详解,彻底讲透 Elasticsearch
一款 IDEA 插件帮你优雅转化 DTO、VO、BO、PO、DO
「开源」数据同步ETL工具,支持多数据源间的增、删、改数据同步
如何使用 SSHGUARD 阻止 SSH 暴力攻击
实时时间序列异常检测
[开源]一套BS架构,支持PC、H5端的开源知识管理系统、知识库系统
后端开发常见层式结构设计:跳表、时间轮、LSM-Tree
16 个有用的带宽监控工具来分析 Linux 中的网络使用情况