search test driven by ip2region service

This commit is contained in:
lionsoul2014 2025-12-07 00:38:14 +08:00
parent b44654230f
commit 6531f3970f
2 changed files with 187 additions and 101 deletions

View File

@ -178,39 +178,40 @@ make
# 查询测试
通过 `xdb_searcher search` 命令来测试 xdb 的查询:
### 查询命令
通过 `./xdb_searcher search` 命令来测试 xdb 的查询:
```bash
➜ golang git:(fr_xdb_ipv6) ✗ ./xdb_searcher search
➜ golang git:(master) ✗ ./xdb_searcher search --help
./xdb_searcher search [command options]
options:
--db string ip2region binary xdb file path
--cache-policy string cache policy: file/vectorIndex/content
--v4-db string ip2region v4 binary xdb file path
--v4-cache-policy string v4 cache policy, default vectorIndex, options: file/vectorIndex/content
--v6-db string ip2region v6 binary xdb file path
--v6-cache-policy string v6 cache policy, default vectorIndex, options: file/vectorIndex/content
--help print this help menu
```
例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 的查询测试
### 参数解析
1. `v4-xdb`: IPv4 的 xdb 文件路径,默认为仓库中的 data/ip2region_v4.xdb
2. `v6-xdb`: IPv6 的 xdb 文件路径,默认为仓库中的 data/ip2region_v6.xdb
3. `v4-cache-policy`: v4 查询使用的缓存策略,默认为 `vectorIndex`可选file/vectorIndex/content
4. `v6-cache-policy`: v6 查询使用的缓存策略,默认为 `vectorIndex`可选file/vectorIndex/content
### 测试 Demo
例如:使用默认的 data/ip2region_v4.xdb 和 data/ip2region_v6.xdb 进行查询测试:
```bash
➜ golang git:(master) ./xdb_searcher search --db=../../data/ip2region_v4.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
➜ golang git:(master) ✗ ./xdb_searcher search
ip2region search service test program
+-v4 db: /data01/code/c/ip2region/data/ip2region_v4.xdb (vectorIndex)
+-v6 db: /data01/code/c/ip2region/data/ip2region_v6.xdb (vectorIndex)
type 'quit' to exit
ip2region>> 219.133.111.87
{region: 中国|广东省|深圳市|电信, ioCount: 2, took: 19.005µs}
ip2region>>
ip2region>> 1.2.3.4
{region: 美国|华盛顿|0|谷歌, took: 69.088µs}
ip2region>> 240e:3b7:3272:d8d0:db09:c067:8d59:539e
{region: 中国|广东省|深圳市|家庭宽带, took: 67.756µs}
ip2region>>
```
例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 的查询:
```bash
➜ golang git:(master) ✗ ./xdb_searcher search --db=../../data/ip2region_v6.xdb
ip2region xdb searcher test program
source xdb: ../../data/ip2region_v6.xdb (IPv6, vectorIndex)
type 'quit' to exit
ip2region>> ::
{region: , ioCount: 1, took: 42.157µs}
ip2region>> 240e:87c:892:ffff:ffff:ffff:ffff:ffff
{region: 中国|北京市|北京市|专线用户, ioCount: 10, took: 88.769µs}
```
输入对应版本的 ip 地址进行查询即可,输入 quit 退出测试程序。可以设置 `cache-policy` 为 file/vectorIndex/content 来测试不同的查询缓存机制。
输入 v4 或者 v6 的 IP 地址即可进行查询测试,也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的查询效果。
# bench 测试

View File

@ -13,13 +13,114 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/lionsoul2014/ip2region/binding/golang/service"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mitchellh/go-homedir"
)
func getXdbPath(fileName string) (string, error) {
binPath, err := os.Executable()
if err != nil {
return "", fmt.Errorf("failed to get executale: %w", err)
}
xdbPath := filepath.Join(filepath.Dir(filepath.Dir(filepath.Dir(binPath))), "/data/", fileName)
_, err = os.Stat(xdbPath)
if err != nil {
return "", nil
}
// fmt.Printf("xdbPath=%s\n", xdbPath)
return xdbPath, nil
}
func createService(v4XdbPath string, v4CachePolicy string, v6XdbPath string, v6CachePolicy string) (*service.Ip2Region, error) {
// try to create v4 config
v4CPolicy, err := service.CachePolicyFromName(v4CachePolicy)
if err != nil {
return nil, fmt.Errorf("parse v4 cache policy: %w", err)
}
v4DbPath, err := homedir.Expand(v4XdbPath)
if err != nil {
return nil, fmt.Errorf("Expand(`%s`): %s", v4XdbPath, err)
}
v4Config, err := service.NewV4Config(v4CPolicy, v4DbPath, 1)
if err != nil {
return nil, fmt.Errorf("NewV4Config: %w", err)
}
// try to create v6 config
v6Policy, err := service.CachePolicyFromName(v6CachePolicy)
if err != nil {
return nil, fmt.Errorf("parse v6 cache policy: %w", err)
}
v6DbPath, err := homedir.Expand(v6XdbPath)
if err != nil {
return nil, fmt.Errorf("Expand(`%s`): %s", v6XdbPath, err)
}
v6Config, err := service.NewV6Config(v6Policy, v6DbPath, 1)
if err != nil {
return nil, fmt.Errorf("NewV6Config: %w", err)
}
return service.NewIp2Region(v4Config, v6Config)
}
func createSearcher(dbPath string, cachePolicy string) (*xdb.Searcher, error) {
handle, err := os.OpenFile(dbPath, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbPath, err)
}
defer handle.Close()
// verify the xdb file
// @Note: do NOT call it every time you create a searcher since this will slow down the search response.
// @see the util.Verify function for details.
err = xdb.Verify(handle)
if err != nil {
return nil, fmt.Errorf("xdb verify: %w", err)
}
// auto-detect the ip version from the xdb header
header, err := xdb.LoadHeader(handle)
if err != nil {
return nil, fmt.Errorf("failed to load header from `%s`: %s", dbPath, err)
}
version, err := xdb.VersionFromHeader(header)
if err != nil {
return nil, fmt.Errorf("failed to detect IP version from `%s`: %s", dbPath, err)
}
switch cachePolicy {
case "nil", "file":
return xdb.NewWithFileOnly(version, dbPath)
case "vectorIndex":
vIndex, err := xdb.LoadVectorIndexFromFile(dbPath)
if err != nil {
return nil, fmt.Errorf("failed to load vector index from `%s`: %w", dbPath, err)
}
return xdb.NewWithVectorIndex(version, dbPath, vIndex)
case "content":
cBuff, err := xdb.LoadContentFromFile(dbPath)
if err != nil {
return nil, fmt.Errorf("failed to load content from '%s': %w", dbPath, err)
}
return xdb.NewWithBuffer(version, cBuff)
default:
return nil, fmt.Errorf("invalid cache policy `%s`, options: file/vectorIndex/content", cachePolicy)
}
}
func printHelp() {
fmt.Printf("ip2region xdb searcher\n")
fmt.Printf("%s [command] [command options]\n", os.Args[0])
@ -30,7 +131,9 @@ func printHelp() {
func testSearch() {
var err error
var dbFile, cachePolicy = "", "vectorIndex"
var help = ""
var v4DbPath, v4CachePolicy = "", "vectorIndex"
var v6DbPath, v6CachePolicy = "", "vectorIndex"
for i := 2; i < len(os.Args); i++ {
r := os.Args[i]
if len(r) < 5 {
@ -41,52 +144,83 @@ func testSearch() {
continue
}
var key, val = "", ""
var sIdx = strings.Index(r, "=")
if sIdx < 0 {
fmt.Printf("missing = for args pair '%s'\n", r)
return
// fmt.Printf("missing = for args pair '%s'\n", r)
// return
key = r[2:]
} else {
key = r[2:sIdx]
val = r[sIdx+1:]
}
switch r[2:sIdx] {
case "db":
dbFile = r[sIdx+1:]
case "cache-policy":
cachePolicy = r[sIdx+1:]
switch key {
case "help":
if val == "" {
help = "true"
} else {
help = val
}
case "v4-db":
v4DbPath = val
case "v4-cache-policy":
v4CachePolicy = val
case "v6-db":
v6DbPath = val
case "v6-cache-policy":
v6CachePolicy = val
default:
fmt.Printf("undefined option `%s`\n", r)
return
}
}
if dbFile == "" {
// check and get the get the default v4 xdb path
if v4DbPath == "" {
v4DbPath, err = getXdbPath("ip2region_v4.xdb")
if err != nil {
fmt.Printf("failed to get v4 xdb path: %s", err)
return
}
}
// check and get the get the default v6 xdb path
if v6DbPath == "" {
v6DbPath, err = getXdbPath("ip2region_v6.xdb")
if err != nil {
fmt.Printf("failed to get v6 xdb path: %s", err)
return
}
}
if v4DbPath == "" || v6DbPath == "" || help == "true" {
fmt.Printf("%s search [command options]\n", os.Args[0])
fmt.Printf("options:\n")
fmt.Printf(" --db string ip2region binary xdb file path\n")
fmt.Printf(" --cache-policy string cache policy: file/vectorIndex/content\n")
fmt.Printf(" --v4-db string ip2region v4 binary xdb file path\n")
fmt.Printf(" --v4-cache-policy string v4 cache policy, default vectorIndex, options: file/vectorIndex/content\n")
fmt.Printf(" --v6-db string ip2region v6 binary xdb file path\n")
fmt.Printf(" --v6-cache-policy string v6 cache policy, default vectorIndex, options: file/vectorIndex/content\n")
fmt.Printf(" --help print this help menu\n")
return
}
dbPath, err := homedir.Expand(dbFile)
// create the search service with the xdb paths and cache policies
ip2region, err := createService(v4DbPath, v4CachePolicy, v6DbPath, v6CachePolicy)
if err != nil {
fmt.Printf("invalid xdb file path `%s`: %s", dbFile, err)
return
}
// create the searcher with the cache policy setting
searcher, err := createSearcher(dbPath, cachePolicy)
if err != nil {
fmt.Printf("failed to create searcher: %s\n", err.Error())
fmt.Printf("failed to create ip2region service: %s\n", err.Error())
return
}
defer func() {
searcher.Close()
ip2region.Close()
fmt.Printf("searcher test program exited, thanks for trying\n")
}()
fmt.Printf(`ip2region xdb searcher test program
source xdb: %s (%s, %s)
fmt.Printf(`ip2region search service test program
+-v4 db: %s (%s)
+-v6 db: %s (%s)
type 'quit' to exit
`, dbPath, searcher.IPVersion().Name, cachePolicy)
`, v4DbPath, v4CachePolicy, v6DbPath, v6CachePolicy)
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("ip2region>> ")
@ -105,11 +239,11 @@ type 'quit' to exit
}
tStart := time.Now()
region, err := searcher.SearchByStr(line)
region, err := ip2region.SearchByStr(line)
if err != nil {
fmt.Printf("\x1b[0;31m{err: %s, ioCount: %d}\x1b[0m\n", err.Error(), searcher.GetIOCount())
fmt.Printf("\x1b[0;31m{err: %s}\x1b[0m\n", err.Error())
} else {
fmt.Printf("\x1b[0;32m{region: %s, ioCount: %d, took: %s}\x1b[0m\n", region, searcher.GetIOCount(), time.Since(tStart))
fmt.Printf("\x1b[0;32m{region: %s, took: %s}\x1b[0m\n", region, time.Since(tStart))
}
}
}
@ -230,55 +364,6 @@ func testBench() {
cachePolicy, count, cost, costs/count/1000)
}
func createSearcher(dbPath string, cachePolicy string) (*xdb.Searcher, error) {
handle, err := os.OpenFile(dbPath, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbPath, err)
}
defer handle.Close()
// verify the xdb file
// @Note: do NOT call it every time you create a searcher since this will slow down the search response.
// @see the util.Verify function for details.
err = xdb.Verify(handle)
if err != nil {
return nil, fmt.Errorf("xdb verify: %w", err)
}
// auto-detect the ip version from the xdb header
header, err := xdb.LoadHeader(handle)
if err != nil {
return nil, fmt.Errorf("failed to load header from `%s`: %s", dbPath, err)
}
version, err := xdb.VersionFromHeader(header)
if err != nil {
return nil, fmt.Errorf("failed to detect IP version from `%s`: %s", dbPath, err)
}
switch cachePolicy {
case "nil", "file":
return xdb.NewWithFileOnly(version, dbPath)
case "vectorIndex":
vIndex, err := xdb.LoadVectorIndexFromFile(dbPath)
if err != nil {
return nil, fmt.Errorf("failed to load vector index from `%s`: %w", dbPath, err)
}
return xdb.NewWithVectorIndex(version, dbPath, vIndex)
case "content":
cBuff, err := xdb.LoadContentFromFile(dbPath)
if err != nil {
return nil, fmt.Errorf("failed to load content from '%s': %w", dbPath, err)
}
return xdb.NewWithBuffer(version, cBuff)
default:
return nil, fmt.Errorf("invalid cache policy `%s`, options: file/vectorIndex/content", cachePolicy)
}
}
func main() {
if len(os.Args) < 2 {
printHelp()