lua c ext is ready for IPv6

This commit is contained in:
lion 2025-09-21 12:56:42 +08:00
parent 13324d1575
commit 29fa8e25ab
4 changed files with 183 additions and 53 deletions

View File

@ -14,30 +14,73 @@ sudo make install
# 使用方式 # 使用方式
### 关于 IPv4 和 IPv6
该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下:
```lua
-- 引入 xdb searcher 扩展
local xdb = require("xdb_searcher")
-- 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件IP版本指定为 IPv4
local db_path = "../../data/ip2region_v4.xdb" -- 或者你的 ipv4 xdb 的路径
local version = xdb.IPv4
-- 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件IP版本指定为 IPv6
local db_path = "../../data/ip2region_v6.xdb"; -- 或者你的 ipv6 xdb 路径
local version = xdb.IPv6
-- db_path 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错
-- 备注:以下演示直接使用 db_path 和 version 变量
```
### XDB 文件验证
建议您主动去验证 xdb 文件的适用性,因为后期的一些新功能可能会导致目前的 Searcher 版本无法适用你使用的 xdb 文件,验证可以避免运行过程中的一些不可预测的错误。 你不需要每次都去验证,例如在服务启动的时候,或者手动调用命令验证确认版本匹配即可,不要在每次创建的 Searcher 的时候运行验证,这样会影响查询的响应速度,尤其是高并发的使用场景。
```lua
local xdb = require("xdb_searcher")
-- verify the xdb
if xdb.verify(db_path) == false then
-- 适用性验证失败!!!
-- 当前查询客户端实现不适用于 db_path 指定的 xdb 文件的查询.
-- 应该停止启动服务,使用合适的 xdb 文件或者升级到适合 db_path 的 Searcher 实现。
print(string.format("failed to verify the xdb file: %s", db_path))
return
end
-- 验证通过,当前使用的 Searcher 可以安全的用于对 db_path 指向的 xdb 的查询操作
```
### 完全基于文件的查询 ### 完全基于文件的查询
```lua ```lua
local xdb = require("xdb_searcher") local xdb = require("xdb_searcher")
-- 1、从 db_path 创建基于文件的 xdb 查询对象 -- 1、使用 version 从 db_path 创建基于文件的 xdb 查询对象
local db_path = "ip2region.xdb file path" local searcher, err = xdb.new_with_file_only(version, db_path)
local searcher, err = xdb.new_with_file_only(db_path)
if err ~= nil then if err ~= nil then
print(string.format("failed to create searcher: %s", err)) print(string.format("failed to create searcher: %s", err))
return return
end end
-- 2、调用查询 API 进行查询 -- 2、调用查询 API 进行查询IPv4 和 IPv6 都支持
local ip_str = "1.2.3.4" local ip_str = "1.2.3.4"
-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6
local s_time = xdb.now() local s_time = xdb.now()
region, err = searcher:search(ip_str) region, err = searcher:search(ip_str)
local c_time = xdb.now() - s_time
if err ~= nil then if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err)) print(string.format("failed to search(%s): %s", ip_str, err))
return return
end end
print(string.format("{region: %s, took: %.5f μs}", region, c_time))
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象 -- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -- 3关闭 xdb 查询器
searcher:close()
--
-- 4模块资源清理仅在需要将整个服务完全关闭前调用
xdb.cleanup()
``` ```
### 缓存 `VectorIndex` 索引 ### 缓存 `VectorIndex` 索引
@ -46,8 +89,6 @@ print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
```lua ```lua
local xdb = require("xdb_searcher") 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 加载一次即可,建议在服务启动的时候加载为全局对象。 -- vectorIndex 加载一次即可,建议在服务启动的时候加载为全局对象。
v_index, err = xdb.load_vector_index(db_path) v_index, err = xdb.load_vector_index(db_path)
@ -57,24 +98,33 @@ if err ~= nil then
end end
-- 2、使用全局的 v_index 创建带 VectorIndex 缓存的查询对象。 -- 2、使用全局的 v_index 创建带 VectorIndex 缓存的查询对象。
searcher, err = xdb.new_with_vector_index(db_path, v_index) searcher, err = xdb.new_with_vector_index(version, db_path, v_index)
if err ~= nil then if err ~= nil then
print(string.format("failed to create vector index searcher: %s", err)) print(string.format("failed to create vector index searcher: %s", err))
return return
end end
-- 3、调用查询 API -- 3、调用查询 API IPv4 和 IPv6 都支持
local ip_str = "1.2.3.4" local ip_str = "1.2.3.4"
-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6
local s_time = xdb.now() local s_time = xdb.now()
region, err = searcher:search(ip_str) region, err = searcher:search(ip_str)
local c_time = xdb.now() = s_time
if err ~= nil then if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err)) print(string.format("failed to search(%s): %s", ip_str, err))
return return
end end
print(string.format("{region: %s, took: %.5f μs}", region, c_time))
-- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象 -- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -- 4关闭 xdb 查询器
searcher:close()
--
-- 5模块资源清理仅在需要将整个服务完全关闭前调用
xdb.cleanup()
``` ```
### 缓存整个 `xdb` 数据 ### 缓存整个 `xdb` 数据
@ -83,36 +133,43 @@ print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
```lua ```lua
local xdb = require("xdb_searcher") local xdb = require("xdb_searcher")
local db_path = "ip2region.xdb file path"
-- 1、从指定的 db_path 加载整个 xdb 到内存。 -- 1、从指定的 db_path 加载整个 xdb 到内存。
-- xdb内容加载一次即可建议在服务启动的时候加载为全局对象。 -- xdb内容加载一次即可建议在服务启动的时候加载为全局对象。
content = xdb.load_content(db_path) local content = xdb.load_content(db_path)
if content == nil then if content == nil then
print(string.format("failed to load xdb content from '%s'", db_path)) print(string.format("failed to load xdb content from '%s'", db_path))
return return
end end
-- 2、使用全局的 content 创建带完全基于内存的查询对象。 -- 2、使用全局的 content 创建带完全基于内存的查询对象。
searcher, err = xdb.new_with_buffer(content) searcher, err = xdb.new_with_buffer(version, content)
if err ~= nil then if err ~= nil then
print(string.format("failed to create content buffer searcher: %s", err)) print(string.format("failed to create content buffer searcher: %s", err))
return return
end end
-- 3、调用查询 API -- 3、调用查询 API IPv4 和 IPv6 都支持
local ip_str = "1.2.3.4" local ip_str = "1.2.3.4"
-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6
local s_time = xdb.now() local s_time = xdb.now()
region, err = searcher:search(ip_str) region, err = searcher:search(ip_str)
local c_time = xdb.now() - s_time
if err ~= nil then if err ~= nil then
print(string.format("failed to search(%s): %s", ip_str, err)) print(string.format("failed to search(%s): %s", ip_str, err))
return return
end end
print(string.format("{region: %s, took: %.5f μs}", region, c_time))
-- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。 -- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。
-- 建议在服务启动的时候创建好全局的 searcher 对象,然后全局并发使用。 -- 建议在服务启动的时候创建好全局的 searcher 对象,然后全局并发使用。
print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) -- 4关闭 xdb 查询器
searcher:close()
--
-- 5模块资源清理仅在需要将整个服务完全关闭前调用
xdb.cleanup()
``` ```
@ -120,21 +177,31 @@ print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time))
通过 `search_test.lua` 脚本来进行查询测试: 通过 `search_test.lua` 脚本来进行查询测试:
```bash ```bash
➜ lua_c git:(lua_binding) ✗ lua ./search_test.lua ➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./search_test.lua
lua search_test.lua [command options] lua search_test.lua [command options]
options: options:
--db string ip2region binary xdb file path --db string ip2region binary xdb file path
--cache-policy string cache policy: file/vectorIndex/content --cache-policy string cache policy: file/vectorIndex/content
``` ```
例如:使用默认的 data/ip2region.xdb 进行查询测试: 例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 查询测试:
```bash ```bash
➜ lua_c git:(lua_binding) ✗ lua ./search_test.lua --db=../../data/ip2region.xdb --cache-policy=vectorIndex ➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./search_test.lua --db=../../data/ip2region_v4.xdb
ip2region xdb searcher test program, cachePolicy: vectorIndex ip2region xdb searcher test program
source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
type 'quit' to exit type 'quit' to exit
ip2region>> 1.2.3.4 ip2region>> 120.229.45.2
{region: 美国|0|华盛顿|0|谷歌, io_count: 7, took: 15μs} {region: 中国|广东省|深圳市|移动, io_count: 3, took: 34μs}
ip2region>> ```
例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 查询测试:
```bash
➜ lua_c git:(fr_lua_c_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:3276:33b0:958f:f34c:d04f:f6a
{region: 中国|广东省|深圳市|家庭宽带, io_count: 14, took: 79μs}
``` ```
输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。 输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。
@ -144,18 +211,24 @@ ip2region>>
通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: 通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能:
```bash ```bash
➜ lua_c git:(lua_binding) ✗ lua ./bench_test.lua ➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua
lua bench_test.lua [command options] lua bench_test.lua [command options]
options: options:
--db string ip2region binary xdb file path --db string ip2region binary xdb file path
--src string source ip text file path --src string source ip text file path
--cache-policy string cache policy: file/vectorIndex/content --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 ```bash
➜ lua_c git:(lua_binding) ✗ lua ./bench_test.lua --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=vectorIndex ➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt
Bench finished, {cachePolicy: vectorIndex, total: 3417955, took: 5.865 s, cost: 1.399 μs/op} Bench finished, {cachePolicy: vectorIndex, total: 1367686, took: 8.593 s, cost: 5.433 μs/op}
```
例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 来进行 IPv6 的 bench 测试:
```bash
➜ lua_c git:(fr_lua_c_ipv6) ✗ lua ./bench_test.lua --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt
Bench finished, {cachePolicy: vectorIndex, total: 34159862, took: 829.008 s, cost: 23.176 μs/op}
``` ```
可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。 可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。

View File

@ -59,7 +59,7 @@ if string.len(dbFile) < 2 then
return return
end end
-- verify the xdb from header -- verify the xdb
if xdb.verify(dbFile) == false then if xdb.verify(dbFile) == false then
print(string.format("failed to verify the xdb file: %s", dbFile)) print(string.format("failed to verify the xdb file: %s", dbFile))
return return
@ -119,7 +119,7 @@ end
print(string.format([[ print(string.format([[
ip2region xdb searcher test program ip2region xdb searcher test program
source xdb: %s (%s, %s) source xdb: %s (%s, %s)
type 'quit' to exit]], dbFile, "IPv4", cachePolicy)) type 'quit' to exit]], dbFile, xdb.version_info(version).name, cachePolicy))
local region, err = "", nil local region, err = "", nil
local ip_int, s_time, c_time = 0, 0, 0 local ip_int, s_time, c_time = 0, 0, 0
while ( true ) do while ( true ) do

View File

@ -65,6 +65,13 @@ function test_load_header()
end end
end end
function test_version_info()
local v4 = xdb.version_info(xdb.IPv4)
print(string.format("{id:%d, name: %s, bytes: %d, segment_index_size: %d}", v4.id, v4.name, v4.bytes, v4.segment_index_size))
local v6 = xdb.version_info(xdb.IPv6)
print(string.format("{id:%d, name: %s, bytes: %d, segment_index_size: %d}", v6.id, v6.name, v6.bytes, v6.segment_index_size))
local vx = xdb.version_info(3)
end
function test_load_vector_index() function test_load_vector_index()
v_index, err = xdb.load_vector_index("../../data/ip2region_v4.xdb") v_index, err = xdb.load_vector_index("../../data/ip2region_v4.xdb")
@ -91,14 +98,26 @@ end
function test_search() function test_search()
-- ipv4
local ip_str = "1.2.3.4" local ip_str = "1.2.3.4"
searcher, err = xdb.new_with_file_only(xdb.IPv4, "../../data/ip2region_v4.xdb") searcher, err = xdb.new_with_file_only(xdb.IPv4, "../../data/ip2region_v4.xdb")
print(string.format("searcher.tostring=%s", searcher))
local t_start = xdb.now() local t_start = xdb.now()
region, err = searcher:search(ip_str) region, err = searcher:search(ip_str)
local c_time = xdb.now() - t_start local c_time = xdb.now() - t_start
print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}", print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}",
ip_str, region, searcher:get_io_count(), c_time, err)) ip_str, region, searcher:get_io_count(), c_time, err))
searcher:close()
-- IPv6
ip_str = "240e:3b7:3276:33b0:958f:f34c:d04f:f6a"
searcher, err = xdb.new_with_file_only(xdb.IPv6, "../../data/ip2region_v6.xdb")
print(string.format("searcher.tostring=%s", searcher)) print(string.format("searcher.tostring=%s", searcher))
t_start = xdb.now()
region, err = searcher:search(ip_str)
c_time = xdb.now() - t_start
print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}",
ip_str, region, searcher:get_io_count(), c_time, err))
searcher:close() searcher:close()
end end

View File

@ -256,6 +256,15 @@ static int lua_xdb_load_content_from_file(lua_State *L) {
return 2; return 2;
} }
static int lua_xdb_verify_from_file(lua_State *L) {
const char *db_path;
luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and the xdb file path expected");
db_path = luaL_checkstring(L, 1);
lua_pushboolean(L, xdb_verify_from_file(db_path) == 0 ? 1 : 0);
return 1;
}
static int lua_xdb_version_from_header(lua_State *L) { static int lua_xdb_version_from_header(lua_State *L) {
xdb_version_t *version; xdb_version_t *version;
xdb_buffer_t *buffer; xdb_buffer_t *buffer;
@ -272,19 +281,46 @@ static int lua_xdb_version_from_header(lua_State *L) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "failed to detect version from header"); lua_pushstring(L, "failed to detect version from header");
} else { } else {
lua_pushlightuserdata(L, version); lua_pushinteger(L, version->id);
lua_pushnil(L); lua_pushnil(L);
} }
return 2; return 2;
} }
static int lua_xdb_verify_from_file(lua_State *L) { static xdb_version_t *_get_version(lua_State *L, int arg) {
const char *db_path; int vid = luaL_checkinteger(L, arg);
if (vid == xdb_ipv4_id) {
return XDB_IPv4;
} else if (vid == xdb_ipv6_id) {
return XDB_IPv6;
} else {
return NULL;
}
}
luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and the xdb file path expected"); static int lua_xdb_version_info(lua_State *L) {
db_path = luaL_checkstring(L, 1); xdb_version_t *version;
lua_pushboolean(L, xdb_verify_from_file(db_path) == 0 ? 1 : 0);
luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via '.' and version id expected");
// check the ip version
version = _get_version(L, 1);
if (version == NULL) {
return luaL_error(L, "invalid verison id specified");
}
lua_newtable(L);
lua_pushinteger(L, version->id);
lua_setfield(L, -2, "id");
lua_pushstring(L, version->name);
lua_setfield(L, -2, "name");
lua_pushinteger(L, version->bytes);
lua_setfield(L, -2, "bytes");
lua_pushinteger(L, version->segment_index_size);
lua_setfield(L, -2, "segment_index_size");
return 1; return 1;
} }
@ -422,7 +458,7 @@ static int lua_xdb_now(lua_State *L) {
// --- xdb searcher api // --- xdb searcher api
static int lua_xdb_new_with_file_only(lua_State *L) { static int lua_xdb_new_with_file_only(lua_State *L) {
int err, vid; int err;
xdb_version_t *version; xdb_version_t *version;
xdb_searcher_t *searcher; xdb_searcher_t *searcher;
const char *db_path = NULL; const char *db_path = NULL;
@ -430,10 +466,9 @@ static int lua_xdb_new_with_file_only(lua_State *L) {
luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb file path expected"); luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb file path expected");
// check the ip version // check the ip version
if (!lua_islightuserdata(L, 1)) { version = _get_version(L, 1);
return luaL_error(L, "invalid verison resouce specified"); if (version == NULL) {
} else { return luaL_error(L, "invalid verison id specified");
version = (xdb_version_t *) lua_touserdata(L, 1);
} }
// check the db path // check the db path
@ -472,10 +507,9 @@ static int lua_xdb_new_with_vector_index(lua_State *L) {
luaL_argcheck(L, lua_gettop(L) == 3, 1, "call via '.', ip version / xdb file path / vector index buffer expected"); luaL_argcheck(L, lua_gettop(L) == 3, 1, "call via '.', ip version / xdb file path / vector index buffer expected");
// check the ip version // check the ip version
if (!lua_islightuserdata(L, 1)) { version = _get_version(L, 1);
return luaL_error(L, "invalid verison resouce specified"); if (version == NULL) {
} else { return luaL_error(L, "invalid verison id specified");
version = (xdb_version_t *) lua_touserdata(L, 1);
} }
// db_path checking // db_path checking
@ -519,10 +553,9 @@ static int lua_xdb_new_with_buffer(lua_State *L) {
luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb content buffer expected"); luaL_argcheck(L, lua_gettop(L) == 2, 1, "call via '.' and ip version / xdb content buffer expected");
// check the ip version // check the ip version
if (!lua_islightuserdata(L, 1)) { version = _get_version(L, 1);
return luaL_error(L, "invalid verison resouce specified"); if (version == NULL) {
} else { return luaL_error(L, "invalid verison id specified");
version = (xdb_version_t *) lua_touserdata(L, 1);
} }
// content buffer checking // content buffer checking
@ -647,7 +680,11 @@ static int lua_xdb_get_io_count(lua_State *L) {
} }
static int lua_xdb_tostring(lua_State *L) { static int lua_xdb_tostring(lua_State *L) {
lua_pushliteral(L, "xdb searcher object"); xdb_searcher_t *searcher;
luaL_argcheck(L, lua_gettop(L) == 1, 1, "call via ':' or xdb searcher was broken");
searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME);
lua_pushfstring(L, "xdb %s searcher object", xdb_get_version(searcher)->name);
return 1; return 1;
} }
@ -677,6 +714,7 @@ static const struct luaL_Reg xdb_searcher_functions[] = {
{"load_content", lua_xdb_load_content_from_file}, {"load_content", lua_xdb_load_content_from_file},
{"verify", lua_xdb_verify_from_file}, {"verify", lua_xdb_verify_from_file},
{"version_from_header", lua_xdb_version_from_header}, {"version_from_header", lua_xdb_version_from_header},
{"version_info", lua_xdb_version_info},
{"cleanup", lua_xdb_cleanup}, {"cleanup", lua_xdb_cleanup},
{"parse_ip", lua_xdb_parse_ip}, {"parse_ip", lua_xdb_parse_ip},
{"ip_to_string", lua_xdb_ip_to_string}, {"ip_to_string", lua_xdb_ip_to_string},
@ -708,9 +746,9 @@ int luaopen_xdb_searcher(lua_State *L)
luaL_setfuncs(L, xdb_searcher_functions, 0); luaL_setfuncs(L, xdb_searcher_functions, 0);
// register the constants attributes // register the constants attributes
lua_pushlightuserdata(L, XDB_IPv4); lua_pushinteger(L, xdb_ipv4_id);
lua_setfield(L, -2, "IPv4"); lua_setfield(L, -2, "IPv4");
lua_pushlightuserdata(L, XDB_IPv6); lua_pushinteger(L, xdb_ipv6_id);
lua_setfield(L, -2, "IPv6"); lua_setfield(L, -2, "IPv6");
lua_pushinteger(L, xdb_header_buffer); lua_pushinteger(L, xdb_header_buffer);
lua_setfield(L, -2, "header_buffer"); lua_setfield(L, -2, "header_buffer");