diff --git a/binding/lua_c/ReadMe.md b/binding/lua_c/ReadMe.md index f1f211e..5b66a6f 100644 --- a/binding/lua_c/ReadMe.md +++ b/binding/lua_c/ReadMe.md @@ -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 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 从 db_path 创建基于文件的 xdb 查询对象 +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" +-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 local s_time = xdb.now() region, err = searcher:search(ip_str) +local c_time = xdb.now() - s_time if err ~= nil then print(string.format("failed to search(%s): %s", ip_str, err)) return end +print(string.format("{region: %s, took: %.5f μs}", region, c_time)) + -- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象 -print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) +-- 3,关闭 xdb 查询器 +searcher:close() + +-- +-- 4,模块资源清理,仅在需要将整个服务完全关闭前调用 +xdb.cleanup() ``` ### 缓存 `VectorIndex` 索引 @@ -46,8 +89,6 @@ 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 对象做成全局变量。 -- vectorIndex 加载一次即可,建议在服务启动的时候加载为全局对象。 v_index, err = xdb.load_vector_index(db_path) @@ -57,24 +98,33 @@ if err ~= nil then end -- 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 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" +-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 local s_time = xdb.now() region, err = searcher:search(ip_str) +local c_time = xdb.now() = s_time if err ~= nil then print(string.format("failed to search(%s): %s", ip_str, err)) return end +print(string.format("{region: %s, took: %.5f μs}", region, c_time)) + -- 备注:并发使用,每个协程需要创建单独的 xdb 查询对象,但是共享全局的 v_index 对象 -print(string.format("{region: %s, took: %.5f μs}", region, xdb.now() - s_time)) +-- 4,关闭 xdb 查询器 +searcher:close() + +-- +-- 5,模块资源清理,仅在需要将整个服务完全关闭前调用 +xdb.cleanup() ``` ### 缓存整个 `xdb` 数据 @@ -83,36 +133,43 @@ 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 到内存。 -- xdb内容加载一次即可,建议在服务启动的时候加载为全局对象。 -content = xdb.load_content(db_path) +local content = xdb.load_content(db_path) if content == nil then print(string.format("failed to load xdb content from '%s'", db_path)) 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" +-- ip_str = "2001:4:112:ffff:ffff:ffff:ffff:ffff" // IPv6 local s_time = xdb.now() region, err = searcher:search(ip_str) +local c_time = xdb.now() - s_time if err ~= nil then print(string.format("failed to search(%s): %s", ip_str, err)) return end +print(string.format("{region: %s, took: %.5f μs}", region, c_time)) + -- 备注:并发使用,用 xdb 整个缓存创建的查询对象可以安全的用于并发。 -- 建议在服务启动的时候创建好全局的 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` 脚本来进行查询测试: ```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] -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_c git:(lua_binding) ✗ lua ./search_test.lua --db=../../data/ip2region.xdb --cache-policy=vectorIndex -ip2region xdb searcher test program, cachePolicy: vectorIndex +➜ lua_c git:(fr_lua_c_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: 15μs} -ip2region>> +ip2region>> 120.229.45.2 +{region: 中国|广东省|深圳市|移动, io_count: 3, took: 34μs} +``` + +例如:使用默认的 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 来测试三种不同缓存实现的效率。 @@ -144,18 +211,24 @@ ip2region>> 通过 `bench_test.lua` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能: ```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] -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 测试: +例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 来进行 IPv4 的 bench 测试: ```bash -➜ lua_c 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: 5.865 s, cost: 1.399 μs/op} +➜ 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: 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 三种不同的缓存实现的的性能。 diff --git a/binding/lua_c/search_test.lua b/binding/lua_c/search_test.lua index 762b0b3..d930900 100644 --- a/binding/lua_c/search_test.lua +++ b/binding/lua_c/search_test.lua @@ -59,7 +59,7 @@ if string.len(dbFile) < 2 then return end --- verify the xdb from header +-- verify the xdb if xdb.verify(dbFile) == false then print(string.format("failed to verify the xdb file: %s", dbFile)) return @@ -119,7 +119,7 @@ end print(string.format([[ ip2region xdb searcher test program 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 ip_int, s_time, c_time = 0, 0, 0 while ( true ) do diff --git a/binding/lua_c/util_test.lua b/binding/lua_c/util_test.lua index eb2b41a..66fcf53 100644 --- a/binding/lua_c/util_test.lua +++ b/binding/lua_c/util_test.lua @@ -65,6 +65,13 @@ function test_load_header() 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() v_index, err = xdb.load_vector_index("../../data/ip2region_v4.xdb") @@ -91,14 +98,26 @@ end function test_search() + -- ipv4 local ip_str = "1.2.3.4" 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() region, err = searcher:search(ip_str) local 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() + + -- 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)) + 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() end diff --git a/binding/lua_c/xdb_searcher.c b/binding/lua_c/xdb_searcher.c index 70f1c99..c342a09 100644 --- a/binding/lua_c/xdb_searcher.c +++ b/binding/lua_c/xdb_searcher.c @@ -256,6 +256,15 @@ static int lua_xdb_load_content_from_file(lua_State *L) { 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) { xdb_version_t *version; xdb_buffer_t *buffer; @@ -272,19 +281,46 @@ static int lua_xdb_version_from_header(lua_State *L) { lua_pushnil(L); lua_pushstring(L, "failed to detect version from header"); } else { - lua_pushlightuserdata(L, version); + lua_pushinteger(L, version->id); lua_pushnil(L); } return 2; } -static int lua_xdb_verify_from_file(lua_State *L) { - const char *db_path; +static xdb_version_t *_get_version(lua_State *L, int arg) { + 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"); - db_path = luaL_checkstring(L, 1); - lua_pushboolean(L, xdb_verify_from_file(db_path) == 0 ? 1 : 0); +static int lua_xdb_version_info(lua_State *L) { + xdb_version_t *version; + + 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; } @@ -422,7 +458,7 @@ static int lua_xdb_now(lua_State *L) { // --- xdb searcher api static int lua_xdb_new_with_file_only(lua_State *L) { - int err, vid; + int err; xdb_version_t *version; xdb_searcher_t *searcher; 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"); // check the ip version - if (!lua_islightuserdata(L, 1)) { - return luaL_error(L, "invalid verison resouce specified"); - } else { - version = (xdb_version_t *) lua_touserdata(L, 1); + version = _get_version(L, 1); + if (version == NULL) { + return luaL_error(L, "invalid verison id specified"); } // 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"); // check the ip version - if (!lua_islightuserdata(L, 1)) { - return luaL_error(L, "invalid verison resouce specified"); - } else { - version = (xdb_version_t *) lua_touserdata(L, 1); + version = _get_version(L, 1); + if (version == NULL) { + return luaL_error(L, "invalid verison id specified"); } // 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"); // check the ip version - if (!lua_islightuserdata(L, 1)) { - return luaL_error(L, "invalid verison resouce specified"); - } else { - version = (xdb_version_t *) lua_touserdata(L, 1); + version = _get_version(L, 1); + if (version == NULL) { + return luaL_error(L, "invalid verison id specified"); } // 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) { - 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; } @@ -677,6 +714,7 @@ static const struct luaL_Reg xdb_searcher_functions[] = { {"load_content", lua_xdb_load_content_from_file}, {"verify", lua_xdb_verify_from_file}, {"version_from_header", lua_xdb_version_from_header}, + {"version_info", lua_xdb_version_info}, {"cleanup", lua_xdb_cleanup}, {"parse_ip", lua_xdb_parse_ip}, {"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); // register the constants attributes - lua_pushlightuserdata(L, XDB_IPv4); + lua_pushinteger(L, xdb_ipv4_id); lua_setfield(L, -2, "IPv4"); - lua_pushlightuserdata(L, XDB_IPv6); + lua_pushinteger(L, xdb_ipv6_id); lua_setfield(L, -2, "IPv6"); lua_pushinteger(L, xdb_header_buffer); lua_setfield(L, -2, "header_buffer");