pure lua script is ready for IPv6

This commit is contained in:
lion 2025-10-20 18:59:39 +08:00
parent 6cb1da2794
commit 541420b8df
4 changed files with 106 additions and 69 deletions

View File

@ -5,30 +5,49 @@
# 使用方式
### 关于 IPv4 和 IPv6
```lua
local xdb = require("xdb_searcher")
-- 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件IP版本指定为 Version.IPv4
local dbPath = "../../data/ip2region_v4.xdb" -- 或者你的 ipv4 xdb 的路径
local version = xdb.IPv4
-- 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件IP版本指定为 Version.IPv6
local dbPath = "../../data/ip2region_v6.xdb" -- 或者你的 ipv6 xdb 路径
local version = xdb.IPv6
-- dbPath 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错
-- 备注:以下演示直接使用 dbPath 和 version 变量
```
### 完全基于文件的查询
```lua
local xdb = require("xdb_searcher")
-- 1、从 db_path 创建基于文件的 xdb 查询对象
local db_path = "ip2region.xdb file path"
local searcher, err = xdb.new_with_file_only(db_path)
-- 1使用上述的 version 和 dbPath 创建完全基于文件的查询对象
local searcher, err = xdb.new_with_file_only(version, db_path)
if err ~= nil then
print(string.format("failed to create searcher: %s", err))
return
end
-- 2、调用查询 API 进行查询
-- 2、调用查询 API 进行查询IPv4 或者 IPv6 的地址都是同一个接口
local ip_str = "1.2.3.4"
-- local ip_str = "240e:3b7:3272:d8d0:db09:c067:8d59:539e" -- IPv6
local s_time = xdb.now()
region, err = searcher:search(ip_str)
region, err = searcher:search_by_string(ip_str)
if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err))
return
end
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象
print(string.format("{region: %s, io_count: %d, took: %.5f μs}", region, searcher:get_io_count(), xdb.now() - s_time))
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
-- 3、关闭资源
searcher:close()
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象
```
### 缓存 `VectorIndex` 索引
@ -37,35 +56,37 @@ print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
```lua
local xdb = require("xdb_searcher")
local db_path = "ip2region.xdb file path"
-- 1、从指定的 db_path 加载 VectorIndex 缓存,把下述的 v_index 对象做成全局变量。
-- 1、从指定的 db_path 加载 vectorIndex 缓存,把下述的 v_index 对象做成全局变量。
-- vectorIndex 加载一次即可,建议在服务启动的时候加载为全局对象。
v_index, err = xdb.load_vector_index(db_path)
v_index, err = xdb.load_vector_index(dbPath)
if err ~= nil then
print(string.format("failed to load vector index from '%s'", db_path))
return
end
-- 2、使用全局的 v_index 创建带 VectorIndex 缓存的查询对象。
searcher, err = xdb.new_with_vector_index(db_path, v_index)
-- 2、使用全局的 v_index 创建带 vectorIndex 缓存的查询对象。
searcher, err = xdb.new_with_vector_index(version, dbPath, v_index)
if err ~= nil then
print(string.format("failed to create vector index searcher: %s", err))
return
end
-- 3、调用查询 API
-- 3、调用查询 APIIPv4 或者 IPv6 都是同一个接口
local ip_str = "1.2.3.4"
-- local ip_str = "240e:3b7:3272:d8d0:db09:c067:8d59:539e" -- IPv6
local s_time = xdb.now()
region, err = searcher:search(ip_str)
region, err = searcher:search_by_string(ip_str)
if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err))
return
end
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象
print(string.format("{region: %s, io_count: %d, took: %.5f μs}", region, searcher:get_io_count(), xdb.now() - s_time))
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
-- 4、关闭资源
searcher:close()
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象
```
### 缓存整个 `xdb` 数据
@ -74,58 +95,70 @@ print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
```lua
local xdb = require("xdb_searcher")
local db_path = "ip2region.xdb file path"
-- 1、从指定的 db_path 加载整个 xdb 到内存。
-- 1、从上述的 dbPath 加载整个 xdb 到内存。
-- xdb内容加载一次即可建议在服务启动的时候加载为全局对象。
content = xdb.load_content(db_path)
if content == nil then
print(string.format("failed to load xdb content from '%s'", db_path))
content, err = xdb.load_content(dbPath)
if err ~= nil then
print(string.format("failed to load xdb content: %s", err))
return
end
-- 2、使用全局的 content 创建带完全基于内存的查询对象。
searcher, err = xdb.new_with_buffer(content)
searcher, err = xdb.new_with_buffer(version, content)
if err ~= nil then
print(string.format("failed to create content buffer searcher: %s", err))
return
end
-- 3、调用查询 API
-- 3、调用查询 APIIPv4 或者 IPv6 都是同一个接口
local ip_str = "1.2.3.4"
-- local ip_str = "240e:3b7:3272:d8d0:db09:c067:8d59:539e" -- IPv6
local s_time = xdb.now()
region, err = searcher:search(ip_str)
region, err = searcher:search_by_string(ip_str)
if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err))
return
end
print(string.format("{region: %s, io_count: %d, took: %.5f μs}", region, searcher:get_io_count(), xdb.now() - s_time))
-- 4、关闭资源 - 该 searcher 对象可以安全用于并发,等整个服务关闭的时候再关闭 searcher
-- searcher:close()
-- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。
-- 建议在服务启动的时候创建好全局的 searcher 对象,然后全局并发使用。
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
```
# 查询测试
通过 `search_test.lua` 脚本来进行查询测试:
通过 `lua search_test.lua` 脚本来进行查询测试:
```bash
➜ lua git:(lua_binding) ✗ lua search_test.lua
➜ lua git:(fr_lua_ipv6) ✗ lua search_test.lua
lua search_test.lua [command options]
options:
options:
--db string ip2region binary xdb file path
--cache-policy string cache policy: file/vectorIndex/content
```
例如:使用默认的 data/ip2region.xdb 进行查询测试:
例如:使用默认的 data/ip2region_v4.xdb 文件进行 IPv4 的查询测试:
```bash
➜ lua git:(lua_binding) ✗ lua search_test.lua --db=../../data/ip2region.xdb --cache-policy=vectorIndex
ip2region xdb searcher test program, cachePolicy: vectorIndex
➜ lua git:(fr_lua_ipv6) ✗ lua search_test.lua --db=../../data/ip2region_v4.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
type 'quit' to exit
ip2region>> 1.2.3.4
{region: 美国|0|华盛顿|0|谷歌, io_count: 7, took: 0μs}
ip2region>>
{region: 美国|华盛顿|0|谷歌, io_count: 7, took: 0μs}
```
例如:使用默认的 data/ip2region_v6.xdb 文件进行 IPv6 的查询测试:
```bash
➜ lua git:(fr_lua_ipv6) ✗ lua search_test.lua --db=../../data/ip2region_v6.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex)
type 'quit' to exit
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|家庭宽带, io_count: 8, took: 0μs}
```
输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。
@ -133,9 +166,9 @@ ip2region>>
# bench 测试
通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能:
通过 `lua bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能:
```bash
➜ lua git:(lua_binding) ✗ lua bench_test.lua
➜ lua git:(fr_lua_ipv6) ✗ lua bench_test.lua
lua bench_test.lua [command options]
options:
--db string ip2region binary xdb file path
@ -143,10 +176,14 @@ options:
--cache-policy string cache policy: file/vectorIndex/content
```
例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 来进行 bench 测试:
例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 文件进行 IPv4 的 bench 测试:
```bash
➜ lua git:(lua_binding) ✗ lua bench_test.lua --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=vectorIndex
Bench finished, {cachePolicy: vectorIndex, total: 3417955, took: 29.000 s, cost: 7.899 μs/op}
lua bench_test.lua --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt
```
例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 文件进行 IPv6 的 bench 测试:
```bash
lua bench_test.lua --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt
```
可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。

View File

@ -122,7 +122,7 @@ end
local lines = handle:lines()
local sip_str, eip_str, s_region, region = "", "", "", ""
local sip, mip, eip, err = 0, 0, 0, 0
local count, c_time = 0, 0, 0
local count, c_time = 0, 0
local s_time = xdb.now()
for l in lines do
if string.len(l) < 1 then
@ -135,9 +135,8 @@ for l in lines do
s_region = v3
break
end
print('sip', sip_str, 'eip', eip_str, 'region', s_region)
-- print('sip', sip_str, 'eip', eip_str, 'region', s_region)
local t_time = xdb.now()
sip, err = xdb.parse_ip(sip_str)
if err ~= nil then
print(string.format("invalid start ip `%s`", sip_str))
@ -155,10 +154,9 @@ for l in lines do
return
end
local t_time = xdb.now()
for _, ip in ipairs({sip, eip}) do
region, err = searcher:search(ip)
print(string.format('search(%s): %s', xdb.ip_to_string(ip), region))
c_time = c_time + xdb.now() - t_time
if err ~= nil then
print(string.format("failed to search ip `%s`", xdb.ip_to_string(ip)))
return
@ -172,6 +170,7 @@ for l in lines do
count = count + 1
end
c_time = c_time + xdb.now() - t_time
::continue::
end
@ -186,6 +185,6 @@ if count > 0 then
avg_costs = c_time / count
end
print(string.format(
"Bench finished, {cachePolicy: %s, total: %d, took: %.3f s, cost: %.3f μs/op}",
cachePolicy, count, total_costs, avg_costs
"Bench finished, {cachePolicy: %s, total: %d, took: {total: %0.3fs, search: %0.3fs}, cost: %.3f μs/op}",
cachePolicy, count, total_costs, c_time/1e6, avg_costs
))

View File

@ -18,7 +18,7 @@ function printHelp()
print(" --cache-policy string cache policy: file/vectorIndex/content")
end
if #arg < 2 then
if #arg < 1 then
printHelp(arg)
return
end
@ -111,31 +111,36 @@ end
-- do the search
print(string.format([[
ip2region xdb searcher test program, cachePolicy: %s
type 'quit' to exit]], cachePolicy))
ip2region xdb searcher test program
source xdb: %s (%s, %s)
type 'quit' to exit]], dbFile, version.name, cachePolicy))
local region, err = "", nil
local ip_int, s_time, c_time = 0, 0, 0
while ( true ) do
local ip_bytes, s_time, c_time = nil, 0, 0
while true do
io.write("ip2region>> ");
io.input(io.stdin);
local line = io.read();
if (line == nil) then
if line == nil then
break
end
if ( line == "quit" ) then
if #line < 1 then
goto continue
end
if line == "quit" then
break
end
ip_int, err = xdb.check_ip(line)
s_time = xdb.now()
ip_bytes, err = xdb.parse_ip(line)
if err ~= nil then
print(string.format("invalid ip address `%s`", line))
print(string.format("failed to parse ip `%s`: %s", line, err))
goto continue
end
-- do the search
s_time = xdb.now()
region, err = searcher:search(line)
region, err = searcher:search(ip_bytes)
if err ~= nil then
print(string.format("{err: %s, io_count: %d}", err, searcher:get_io_count()))
else

View File

@ -132,7 +132,6 @@ function xdb:search(ip_bytes)
-- binary search to get the data
local index_size, ip_sub_compare = version.index_size, version.ip_sub_compare
local bytes, d_bytes = version.bytes, version.bytes << 1
print("index_size", index_size, "bytes", bytes, "d_bytes", d_bytes)
local data_ptr, data_len, p = 0, 0, 0
local sip, eip, err, buff = 0, 0, ""
local l, m, h = 0, 0, (e_ptr - s_ptr) / index_size
@ -146,13 +145,6 @@ function xdb:search(ip_bytes)
return "", string.format("read segment index at %d", p)
end
print(string.format(
"{ip=%s, sip=%s, eip=%s}",
xdb.ip_to_string(ip_bytes),
xdb.ip_to_string(string.sub(buff, 1, bytes)),
xdb.ip_to_string(string.sub(buff, bytes + 1, d_bytes))
))
-- check the index
if ip_sub_compare(ip_bytes, buff, 1) < 0 then
h = m - 1
@ -166,7 +158,7 @@ function xdb:search(ip_bytes)
end
-- matching nothing interception
print(string.format("data_len=%d, data_ptr=%d", data_len, data_ptr))
-- print(string.format("data_len=%d, data_ptr=%d", data_len, data_ptr))
if data_len == 0 then
return "", nil
end
@ -208,6 +200,10 @@ function xdb:read(offset, length)
return buff, nil
end
function xdb:get_ip_version()
return self.version
end
function xdb:get_io_count()
return self.io_count
end
@ -533,7 +529,7 @@ end
--
-- ip bytes compare
function xdb.ip_sub_compare(ip1, buff, offset)
local ip2 = string.sub(buff, offset, offset + #ip1)
local ip2 = string.sub(buff, offset, offset + #ip1 - 1)
if ip1 > ip2 then
return 1
elseif ip1 < ip2 then