Skip to content

Commit

Permalink
Merge pull request #282 from leihua996/master
Browse files Browse the repository at this point in the history
add xdb erlang implementation
  • Loading branch information
lionsoul2014 authored Jan 29, 2023
2 parents 1e88ed8 + f053e97 commit decee31
Show file tree
Hide file tree
Showing 15 changed files with 627 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ target
/maker/golang/dbmaker
/maker/golang/xdb_maker
/maker/golang/golang

#erlang
/binding/erlang/_build
/binding/erlang/doc
129 changes: 129 additions & 0 deletions binding/erlang/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# ip2region xdb erlang 查询客户端

### 简介
该bingding以erlang语言实现xdb查询客户端,基于Erlang OTP Application,查询逻辑由ip2region_worker工作进程实现,支持配多个工作进程来进行负载均衡。

### 应用配置
该应用可配置的参数在ip2region.app.src中,如下:
``` erlang
{env,[
{poolargs, [
{size, 1}, %% 工作进程默认数量
{max_overflow, 5} %% 工作进程最大数量
]}
]}
```

### 编译

```
$ rebar3 compile
```
### 运行
将xdb文件放到priv目录下,然后启动erlang节点:
```
$ rebar3 shell
```
在erlang shell中调用xdb:search/1接口查询Ip地址信息, 该接口支持以list格式字符串、bianry格式字符串、tuple和整数表示的IP地址,如下:
```
1> xdb:search("1.0.8.0").
[20013,22269,124,48,124,24191,19996,30465,124,24191,24030,
24066,124,30005,20449]
2>
3> io:format("~ts~n", [xdb:search("1.0.8.0")]).
中国|0|广东省|广州市|电信
io:format("~ts~n", [xdb:search(<<"1.0.8.0">>)]).
中国|0|广东省|广州市|电信
4> io:format("~ts~n", [xdb:search({1,0,8,0})]).
中国|0|广东省|广州市|电信
6> io:format("~ts~n", [xdb:search(16779264)]).
中国|0|广东省|广州市|电信
```

### 使用方法
* 在rebar.config中引入依赖
```
{deps, [
ip2region
]}.
```
* 启动ip2region Application
```
......
application:ensure_started(ip2region),
......
```

* 调用xdb:search/1接口查询IP信息
```
......
ip2region:search("1.0.8.0"),
......
```

### 单元测试

```
$ rebar3 eunit
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling ip2region
===> Performing EUnit tests...
=INFO REPORT==== 17-Jan-2023::11:52:59.920155 ===
XdbFile:/home/admin/erl-workspace/ip2region/binding/erlang/_build/test/lib/ip2region/priv/ip2region.xdb
....
Finished in 0.074 seconds
4 tests, 0 failures
```

### 基准测试
```
$ cd benchmarks/
$ sh xdb-benchmark.sh
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling ip2region
Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit]
Eshell V12.3.2.2 (abort with ^G)
1> =INFO REPORT==== 17-Jan-2023::11:37:35.631095 ===
XdbFile:/home/admin/erl-workspace/ip2region/binding/erlang/_build/default/lib/ip2region/priv/ip2region.xdb
===> Booted ip2region
===> Evaluating: "xdb_benchmark:main(\"../../data/ip.merge.txt\"), init:stop()."
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 4.835593s
start run benchmark tests
search from file:
ip count:683844,
total time: 28.201699s,
search 24248.326315375536 times per second,
use 41.23995969841075 micro second per search
search from cache:
ip count:683844,
total time: 0.671801s,
search 1017926.4395259906 times per second,
use 0.9823892583688677 micro second per search
benchmark test finish
```
5 changes: 5 additions & 0 deletions binding/erlang/benchmarks/xdb-benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

cd ..

rebar3 shell --eval="xdb_benchmark:main(\"../../data/ip.merge.txt\"), init:stop()."
26 changes: 26 additions & 0 deletions binding/erlang/include/ip2region.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-ifndef(IP2REGION_HRL).
-define(IP2REGION_HRL, true).

-define(NONE, none).
-define(APP_NAME, ip2region).

-define(XDB_VECTOR_INDEX, ets_xdb_vector_index).
-define(XDB_SEGMENT_INDEX, ets_xdb_segement_index).
-define(IP2REGION_CACHE, ets_ip2region_cache).


-define(XDB_HEADER_SIZE, 256).
-define(XDB_VECTOR_COLS, 256).
-define(XDB_VECTOR_INDEX_SIZE, 8).
-define(XDB_VECTOR_INDEX_COUNT, (16#10000)). %% 256*256

-define(XDB_SEGMENT_INDEX_SIZE, 14).

-define(IP2REGION_POOL, ip2region_pool).

-ifndef(IF).
-define(IF(C, T, F), case (C) of true -> (T); false -> (F) end).
-define(IF(C, T), ?IF(C, T, skip)).
-endif.

-endif.
Empty file added binding/erlang/priv/dummy
Empty file.
24 changes: 24 additions & 0 deletions binding/erlang/rebar.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{erl_opts, [
debug_info,
export_all,
nowarn_export_all
]}.

{plugins, [rebar3_hex, rebar3_ex_doc]}.

{deps, [
poolboy
]}.

{shell, [
% {config, "config/sys.config"},
{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}]}.
8 changes: 8 additions & 0 deletions binding/erlang/rebar.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{"1.2.0",
[{<<"poolboy">>,{pkg,<<"poolboy">>,<<"1.5.2">>},0}]}.
[
{pkg_hash,[
{<<"poolboy">>, <<"392B007A1693A64540CEAD79830443ABF5762F5D30CF50BC95CB2C1AAAFA006B">>}]},
{pkg_hash_ext,[
{<<"poolboy">>, <<"DAD79704CE5440F3D5A3681C8590B9DC25D1A561E8F5A9C995281012860901E3">>}]}
].
20 changes: 20 additions & 0 deletions binding/erlang/src/ip2region.app.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{application, ip2region,
[{description, "ip2region xdb client application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {ip2region_app, []}},
{applications,
[kernel,
stdlib
]},
{env,[
{poolargs, [
{size, 1},
{max_overflow, 5}
]}
]},
{modules, []},

{licenses, ["Apache-2.0"]},
{links, [{"Github", "https://github.com/leihua996/ip2region/tree/master/binding/erlang"}]}
]}.
22 changes: 22 additions & 0 deletions binding/erlang/src/ip2region_app.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% @end
%%%-------------------------------------------------------------------

-module(ip2region_app).

-behaviour(application).

-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
ip2region_sup:start_link().

stop(_State) ->
ok.

%% internal functions
55 changes: 55 additions & 0 deletions binding/erlang/src/ip2region_sup.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc ip2region top level supervisor.
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_sup).
-behaviour(supervisor).
-include("ip2region.hrl").

-export([start_link/0]).

-export([init/1, create_table/0]).

-define(SERVER, ?MODULE).

start_link() ->
{ok, SupPid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []),
{ok, _PoolPid} = start_ip2region_pool(SupPid),
{ok, SupPid}.

%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
create_table(),
SupFlags = #{strategy => one_for_one,
intensity => 10,
period => 5},
ChildSpecs = [],
{ok, {SupFlags, ChildSpecs}}.

%% internal functions
%%
create_table() ->
Opts = [named_table, set, public, {read_concurrency, true}, {keypos, 1}],
ets:new(?XDB_VECTOR_INDEX, Opts),
ets:new(?XDB_SEGMENT_INDEX, Opts),
ets:new(?IP2REGION_CACHE, Opts).

start_ip2region_pool(Sup) ->
{ok, PoolArgsCfg} = application:get_env(poolargs),
PoolName = ?IP2REGION_POOL,
PoolArgs = [{strategy, fifo}, {name, {local, PoolName}}, {worker_module, ip2region_worker} | PoolArgsCfg],
WorkerArgs = [],
ChildSpecs = poolboy:child_spec(PoolName, PoolArgs, WorkerArgs),
supervisor:start_child(Sup, ChildSpecs).
26 changes: 26 additions & 0 deletions binding/erlang/src/ip2region_util.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
%%%-------------------------------------------------------------------
%% Copyright 2022 The Ip2Region Authors. All rights reserved.
%% Use of this source code is governed by a Apache2.0-style
%% license that can be found in the LICENSE file.
%%
%% @doc
%% ip2region utils
%% @end
%%%-------------------------------------------------------------------
-module(ip2region_util).
-export([ipv4_to_n/1]).


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) ->
case inet_parse:address(Ip) of
{ok, Addr} ->
ipv4_to_n(Addr);
_ ->
{error, bad_ip_format}
end.
Loading

0 comments on commit decee31

Please sign in to comment.