From 541420b8df1148881594d6edb7f261bf1d7b66ad Mon Sep 17 00:00:00 2001 From: lion Date: Mon, 20 Oct 2025 18:59:39 +0800 Subject: [PATCH] pure lua script is ready for IPv6 --- binding/lua/ReadMe.md | 119 +++++++++++++++++++++++------------ binding/lua/bench_test.lua | 13 ++-- binding/lua/search_test.lua | 27 ++++---- binding/lua/xdb_searcher.lua | 16 ++--- 4 files changed, 106 insertions(+), 69 deletions(-) diff --git a/binding/lua/ReadMe.md b/binding/lua/ReadMe.md index c3a97a3..379c0d1 100644 --- a/binding/lua/ReadMe.md +++ b/binding/lua/ReadMe.md @@ -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、调用查询 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 查询对象,但是共享全局的 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、调用查询 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 +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 三种不同的缓存实现的的性能。 diff --git a/binding/lua/bench_test.lua b/binding/lua/bench_test.lua index 4febe76..0fded45 100644 --- a/binding/lua/bench_test.lua +++ b/binding/lua/bench_test.lua @@ -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 )) diff --git a/binding/lua/search_test.lua b/binding/lua/search_test.lua index fde001e..a7c1b77 100644 --- a/binding/lua/search_test.lua +++ b/binding/lua/search_test.lua @@ -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 diff --git a/binding/lua/xdb_searcher.lua b/binding/lua/xdb_searcher.lua index 9e0da9f..9fc9c9c 100644 --- a/binding/lua/xdb_searcher.lua +++ b/binding/lua/xdb_searcher.lua @@ -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