增加单元测试及基准测试

This commit is contained in:
LeiHua 2023-01-13 21:41:43 +08:00
parent 4efa1b36b2
commit de4f5c0c22
11 changed files with 684077 additions and 31 deletions

1
.gitignore vendored
View File

@ -84,3 +84,4 @@ target
#erlang
/binding/erlang/_build
/binding/erlang/doc

View File

@ -22,3 +22,78 @@ $ rebar3 shell
region => <<>>}
2>
```
### 使用方法
* 在rebar.config中引入依赖
```
{deps, [
ip2region
]}.
```
* 启动ip2region Application
```
......
application:ensure_started(ip2region),
......
```
* 调用ip2region:search/1接口查询IP信息
```
......
ip2region:search("1.0.8.0"),
......
```
### 单元测试
```
$ rebar3 eunit
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling ip2region
===> Performing EUnit tests...
=INFO REPORT==== 13-Jan-2023::21:26:15.137021 ===
XdbFile:/home/admin/erl-workspace/ip2region/binding/erlang/_build/test/lib/ip2region/priv/ip2region.xdb
...
Finished in 0.085 seconds
3 tests, 0 failures
```
### 基准测试
```
$ cd benchmarks/
$ sh ip2region-benchmark.sh
CPU info:
model name : AMD EPYC 7K62 48-Core Processor
cache size : 512 KB
cpu MHz : 2595.124
bogomips : 5190.24
cores/threads : 2
Erlang info:
system_version:Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit]
load test data use 2.724344s
start run benchmark tests
search from file:
ip count:683844,
total time: 25.56304s,
search 26751.27840820184 times per second,
use 37.381391077497206 micro second per search
search from cache:
ip count:683844,
total time: 0.670307s,
search 1020195.2239794602 times per second,
use 0.9802045495756342 micro second per search
benchmark test finish
```

View File

@ -0,0 +1,7 @@
#!/bin/bash
awk -v FS='|' '{print $1}' ../../../data/ip.merge.txt > test_data.txt
cd ..
rebar3 shell --eval="ip2region_benchmark:main(\"./benchmarks/test_data.txt\"), init:stop()."

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,13 @@
{erl_opts, [debug_info, export_all, nowarn_export_all]}.
{erl_opts, [
debug_info,
export_all,
nowarn_export_all
]}.
{plugins, [rebar3_hex, rebar3_ex_doc]}.
{deps, [
poolboy
poolboy
]}.
{shell, [
@ -8,3 +15,10 @@
{apps, [ip2region]}
]}.
{ex_doc, [
{extras, ["README.md"]},
{main, "README.md"},
{source_url, "https://github.com/leihua996/ip2region/tree/master/binding/erlang"}
]}.
{hex, [{doc, ex_doc}]}.

View File

@ -1,5 +1,5 @@
{application, ip2region,
[{description, "An OTP application"},
[{description, "ip2region xdb client application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {ip2region_app, []}},
@ -16,5 +16,5 @@
{modules, []},
{licenses, ["Apache-2.0"]},
{links, []}
{links, [{"Github", "https://github.com/leihua996/ip2region/tree/master/binding/erlang"}]}
]}.

View File

@ -1,3 +1,11 @@
%%%===============================================================
%%% @author leihua <leihua918@sina.com>
%%% @doc
%%% ip2region xdb
%%% Created: 2023-1-13 16:53
%%% @end
%%%===============================================================
-module(ip2region).
-include("ip2region.hrl").
@ -5,23 +13,18 @@
-spec search(Ip :: tuple() | list() | binary()) -> Result :: {error, Reason::atom()} | map().
search(Ip) ->
case check_ip(Ip) of
{false, Reason} -> {error, Reason};
_ ->
Worker = poolboy:checkout(?IP2REGION_POOL, true, infinity),
try
ip2region_worker:search(Worker, Ip)
after
poolboy:checkin(?IP2REGION_POOL, Worker)
end
end.
check_ip({_A, _B, _C, _D}) -> true;
check_ip(Ip) when is_list(Ip); is_binary(Ip) ->
IpRegx = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$",
case re:run(Ip, IpRegx) of
{match, _Captured} ->
ok;
case util:ipv4_to_n(Ip) of
IntIp when is_integer(IntIp) ->
case ets:lookup(?IP2REGION_CACHE, IntIp) of
[{_IntIp, Region}] -> Region;
_ ->
{false, bad_ip_format}
end.
Worker = poolboy:checkout(?IP2REGION_POOL, true, infinity),
try
ip2region_worker:search(Worker, IntIp)
after
poolboy:checkin(?IP2REGION_POOL, Worker)
end
end;
Ret ->
Ret
end.

View File

@ -0,0 +1,68 @@
%%%===============================================================
%%% @author leihua <leihua918@sina.com>
%%% @doc
%%% ip2region性能基准测试
%%% Created: 2023-1-13 17:46
%%% @end
%%%===============================================================
-module(ip2region_benchmark).
-export([main/1]).
main(DataFile) ->
application:ensure_started(ip2region),
show_hw_sw_info(),
IpList = load_test_data(DataFile),
run(IpList).
show_hw_sw_info() ->
io:format("CPU info:~n", []),
io:format("~s", [os:cmd("egrep '^model name' /proc/cpuinfo | head -1")]),
io:format("~s", [os:cmd("egrep '^cache' /proc/cpuinfo | head -1")]),
io:format("~s", [os:cmd("egrep '^cpu MHz' /proc/cpuinfo | head -1")]),
io:format("~s", [os:cmd("egrep '^bogomips' /proc/cpuinfo | head -1")]),
io:format("cores/threads : ~s~n", [os:cmd("egrep -c '^processor' /proc/cpuinfo")]),
io:format("Erlang info:~n", []),
io:format("system_version:~s", [erlang:system_info(system_version)]),
ok.
load_test_data(DataFile) ->
{ok, Fd} = file:open(DataFile, [read]),
T0 = os:timestamp(),
IpList = load_test_data(Fd, []),
T1 = os:timestamp(),
Sec = timer:now_diff(T1, T0) / 1000000,
io:format("load test data use ~ps~n", [Sec]),
IpList.
load_test_data(Fd, IpList) ->
case file:read_line(Fd) of
{ok, Ip} ->
load_test_data(Fd, [string:trim(Ip)| IpList]);
_ ->
file:close(Fd),
IpList
end.
run(IpList) ->
garbage_collect(),
io:format("~nstart run benchmark tests~n", []),
io:format("~nsearch from file:~n", []),
run_test(IpList),
io:format("~nsearch from cache:~n", []),
run_test(IpList),
io:format("~nbenchmark test finish~n", []).
run_test(IpList) ->
T0 = os:timestamp(),
run_test_aux(IpList),
T1 = os:timestamp(),
Sec = timer:now_diff(T1, T0) / 1000000,
IpCount = length(IpList),
io:format("ip count:~p,~ntotal time: ~ps,~nsearch ~p times per second,~nuse ~p micro second per search~n",
[IpCount, Sec, IpCount / Sec, Sec * 1000000/IpCount]).
run_test_aux([]) -> ok;
run_test_aux([Ip | Tail]) ->
#{} = ip2region:search(Ip),
run_test_aux(Tail).

View File

@ -1,8 +1,14 @@
%%%===============================================================
%%% @author leihua <leihua918@sina.com>
%%% @doc
%%% ip2region工作进程
%%% Created: 2023-1-13 16:53
%%% @end
%%%===============================================================
-module(ip2region_worker).
-behaviour(gen_server).
-include("ip2region.hrl").
%% API
-export([start/1, stop/1, start_link/1]).
-export([search/2]).
@ -31,7 +37,12 @@ search(Pid, Ip) ->
%% gen_server callbacks
%% =========================================
init(_Args) ->
PrivDir = code:priv_dir(?APP_NAME),
AppName =
case application:get_application() of
{ok, AName} -> AName;
_ -> ?APP_NAME
end,
PrivDir = code:priv_dir(AppName),
XdbFileName = filename:join([PrivDir, "ip2region.xdb"]),
error_logger:info_report(io_lib:format("XdbFile:~s~n", [XdbFileName])),
{ok, IoDevice} = file:open(XdbFileName, [read, binary]),

View File

@ -2,15 +2,16 @@
-export([ipv4_to_n/1]).
ip_aton(Ip) ->
{ok, Addr} = inet_parse:address(Ip),
Addr.
ipv4_to_n(IntIp) when is_integer(IntIp) -> IntIp;
ipv4_to_n({A, B, C, D}) ->
<<N:32>> = <<A, B, C, D>>,
N;
ipv4_to_n(Ip) when is_binary(Ip) ->
ipv4_to_n(binary_to_list(Ip));
ipv4_to_n(Ip) when is_list(Ip) ->
Addr = ip_aton(Ip),
ipv4_to_n(Addr).
case inet_parse:address(Ip) of
{ok, Addr} ->
ipv4_to_n(Addr);
_ ->
{error, bad_ip_format}
end.

View File

@ -0,0 +1,22 @@
-module(ip2region_test).
-include_lib("eunit/include/eunit.hrl").
search_test_() ->
application:ensure_started(ip2region),
A = #{
city => <<"广州市"/utf8>>,
country => <<"中国"/utf8>>,
isp => <<"电信"/utf8>>,
province => <<"广东省"/utf8>>,
region => <<>>
},
Region1 = ip2region:search("1.0.8.0"),
Region2 = ip2region:search({1,0,8,0}),
Region3 = ip2region:search("xxx.0.8.0"),
[
?_assert(A =:= Region1),
?_assert(A =:= Region2),
?_assert({error, bad_ip_format} =:= Region3)
].