-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d890024
Showing
16 changed files
with
1,864 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
vendor/ | ||
.idea/ | ||
.vscode/ | ||
czip.txt | ||
/pb/build.sh | ||
config.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# cz88 | ||
|
||
基于 cz88 纯真IP数据库开发的 IP 解析服务 - 支持 http 协议请求或 rpc 协议请求,也支持第三方包的方式引入直接使用 | ||
|
||
- Go 语言编写 | ||
- 进程内缓存结果,重复的 ip 查询响应时间平均为 0.2 ms | ||
- 支持部署后通过 http 协议或 rpc 协议请求服务 | ||
- 支持第三方包的方式引入直接使用 | ||
- 内置协程数、缓存数、CPU核心数等指标上报(未开放) | ||
|
||
|
||
## czip.txt 文件获取方式 | ||
1. 通过直接安装纯真网络客户端并将其导出为 txt 文件而获得。将其命名为 `czip.txt` 并放置在根目录下即可 | ||
2. github releases 下载地址:[czip.zip](https://github.com/nuominmin/cz88/releases/download/v1.0.0/czip.zip) 。下载得到 `czip.zip`,解压得到 `czip.txt` 并放置在根目录下即可 | ||
|
||
## 第三方包引入 | ||
|
||
```go | ||
package main | ||
import ( | ||
"fmt" | ||
cz88 "github.com/nuominmin/cz88/core" | ||
) | ||
func main() { | ||
fmt.Println(cz88.GetIpInfo("210.35.117.200")) | ||
} | ||
``` | ||
|
||
## 部署方法 | ||
|
||
### 编译安装 | ||
1. 安装 golang 环境。建议 go1.13 以上。 | ||
2. 编译运行 | ||
|
||
```shell | ||
go mod download | ||
go get github.com/nuominmin/cz88 | ||
go build -o ./cz88 github.com/nuominmin/cz88/cmd | ||
./cz88 | ||
``` | ||
|
||
## http 协议请求 | ||
|
||
| 接口 | 请求方式 | 请求字段 | 说明 | | ||
| :---- | :---- | :---- | :---- | | ||
| /v1/address | GET | ip | 查询 ip 地址相关信息 或 获取访问者自身 ip 地址相关信息(请求字段为空) | | ||
| /v1/my_address | GET | | 获取访问者自身 ip 地址相关信息 | | ||
|
||
## rpc 协议请求 | ||
|
||
请根据 rpc 客户端语言编译 pb/v1/service.proto 得到 pb 文件 | ||
|
||
### go rpc client demo | ||
``` | ||
func GetIpInfo() (*v1.AddressResp, error) { | ||
cc, err := grpc.Dial("127.0.0.1:8108", grpc.WithInsecure()) | ||
if err != nil { | ||
panic(err) | ||
} | ||
resp, err := v1.NewAppClient(cc).Address(context.TODO(), &v1.AddressReq{ | ||
Ip: "", | ||
}) | ||
if err != nil { | ||
errStatus, _ := status.FromError(err) | ||
if errStatus.Code() == codes.Unavailable { | ||
return nil, errors.New("No connection could be made because the target machine actively refused it. ") | ||
} | ||
return nil, errors.New(errStatus.Message()) | ||
} | ||
return resp, nil | ||
} | ||
``` | ||
|
||
## 性能测试 | ||
|
||
1. 随机 ip 请求测试中,首次响应时长约 50ms/请求,产生缓存后约 0.2 ms/请求 | ||
2. 本人开发机压测结果如下所示 | ||
|
||
```shell | ||
min@Rytia-Envy13 | ||
------------------ | ||
OS: Ubuntu 20.04.1 LTS on Windows 10 x86_64 | ||
Kernel: 4.4.0-19041-Microsoft | ||
Uptime: 24 mins | ||
Packages: 761 (dpkg) | ||
Shell: zsh 5.8 | ||
Terminal: /dev/tty2 | ||
CPU: Intel i5-8250U (8) @ 1.800GHz | ||
Memory: 6088MiB / 8038MiB | ||
|
||
# ab -c 100 -t 10 "http://127.0.0.1:8107/v1/address?ip=210.35.117.200" | ||
This is ApacheBench, Version 2.3 <$Revision: 1843412 $> | ||
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ | ||
Licensed to The Apache Software Foundation, http://www.apache.org/ | ||
|
||
Benchmarking 127.0.0.1 (be patient) | ||
Completed 5000 requests | ||
Completed 10000 requests | ||
Completed 15000 requests | ||
Finished 17113 requests | ||
|
||
|
||
Server Software: | ||
Server Hostname: 127.0.0.1 | ||
Server Port: 8107 | ||
|
||
Document Path: /v1/address?ip=210.35.117.200 | ||
Document Length: 114 bytes | ||
|
||
Concurrency Level: 100 | ||
Time taken for tests: 10.041 seconds | ||
Complete requests: 17113 | ||
Failed requests: 0 | ||
Total transferred: 4073370 bytes | ||
HTML transferred: 1951110 bytes | ||
Requests per second: 1704.25 [#/sec] (mean) | ||
Time per request: 58.677 [ms] (mean) | ||
Time per request: 0.587 [ms] (mean, across all concurrent requests) | ||
Transfer rate: 396.15 [Kbytes/sec] received | ||
|
||
Connection Times (ms) | ||
min mean[+/-sd] median max | ||
Connect: 0 18 13.8 14 92 | ||
Processing: 1 40 20.8 36 197 | ||
Waiting: 1 31 17.4 27 180 | ||
Total: 1 58 22.8 53 206 | ||
|
||
Percentage of the requests served within a certain time (ms) | ||
50% 53 | ||
66% 62 | ||
75% 70 | ||
80% 76 | ||
90% 91 | ||
95% 104 | ||
98% 116 | ||
99% 123 | ||
100% 206 (longest request) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
|
||
"cz88/internal/server/http" | ||
"cz88/internal/server/rpc" | ||
|
||
"cz88/internal/service" | ||
) | ||
|
||
func init() { | ||
fmt.Printf("IP num: %d\n", service.GetMetricsNum().Data) | ||
} | ||
|
||
func main() { | ||
go rpc.New() | ||
go http.New() | ||
select {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package config | ||
|
||
import ( | ||
"io/ioutil" | ||
|
||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
var viper = new(Yaml) | ||
|
||
// GetInstance 获取实例 | ||
func GetInstance() *Yaml { | ||
return viper | ||
} | ||
|
||
type TLoadYaml struct { | ||
Path string // ./ 文件路径 | ||
Ext string // .yaml 文件扩展名 | ||
FileName string // config 默认配置文件名 | ||
} | ||
|
||
func NewLoadYaml() *TLoadYaml { | ||
return &TLoadYaml{ | ||
Path: "./", | ||
Ext: ".yaml", | ||
FileName: "config", | ||
} | ||
} | ||
|
||
func init() { | ||
defaultYamlConfig() | ||
_ = NewLoadYaml().InitConfig() | ||
} | ||
|
||
// Yaml config | ||
type Yaml struct { | ||
Http string `yaml:"http"` | ||
Rpc string `yaml:"rpc"` | ||
CZip CZip `yaml:"czip"` | ||
} | ||
|
||
type CZip struct { | ||
FilePath string `yaml:"file_path"` | ||
Charset string `yaml:"charset"` | ||
} | ||
|
||
func (me *TLoadYaml) GetFileName() string { | ||
return me.Path + me.FileName + me.Ext | ||
} | ||
|
||
func defaultYamlConfig() { | ||
viper = &Yaml{ | ||
Http: "127.0.0.1:8107", | ||
Rpc: "127.0.0.1:8108", | ||
CZip: CZip{ | ||
FilePath: "czip.txt", | ||
Charset: "gb18030", | ||
}, | ||
} | ||
} | ||
|
||
func (me *TLoadYaml) InitConfig() error { | ||
yamlFile, err := ioutil.ReadFile(me.GetFileName()) | ||
if err != nil { | ||
return err | ||
} | ||
if err = yaml.Unmarshal(yamlFile, GetInstance()); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package core | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestCalcIP(t *testing.T) { | ||
res := calcIP("255.255.255.255") | ||
fmt.Println("res", res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package core | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
|
||
"cz88/config" | ||
|
||
"github.com/nuominmin/mahonia" | ||
) | ||
|
||
type IpInfoItem struct { | ||
IP string `json:"ip"` | ||
Area string `json:"area"` | ||
Isp string `json:"isp"` | ||
IpStart int64 `json:"-"` | ||
IpEnd int64 `json:"-"` | ||
} | ||
|
||
type IpData struct { | ||
List []IpInfoItem | ||
ListLength int | ||
Cache sync.Map | ||
} | ||
|
||
var ipData *IpData | ||
|
||
// 加载 IP 地址库 | ||
func LoadIpData() *IpData { | ||
if ipData == nil { | ||
path := filepath.Dir(os.Args[0]) + "/" + config.GetInstance().CZip.FilePath | ||
fp, err := os.Open(path) | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
defer fp.Close() | ||
decoder := mahonia.NewDecoder(config.GetInstance().CZip.Charset) | ||
if decoder == nil { | ||
panic("编码不存在") | ||
} | ||
|
||
ipData = &IpData{ | ||
List: []IpInfoItem{}, | ||
} | ||
|
||
reader := bufio.NewReader(fp) | ||
for { | ||
line, err := reader.ReadString('\n') | ||
if err == io.EOF { | ||
fmt.Println("IP 地址加载完成") | ||
break | ||
} | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
lineSlice := strings.Fields(decoder.ConvertString(line)) | ||
|
||
if len(lineSlice) < 3 || lineSlice[0] == "IP数据库共有数据" { | ||
continue | ||
} | ||
|
||
ipItem := IpInfoItem{ | ||
Area: lineSlice[2], | ||
Isp: strings.Trim(fmt.Sprint(lineSlice[3:]), "[]"), | ||
IpStart: calcIP(lineSlice[0]), | ||
IpEnd: calcIP(lineSlice[1]), | ||
} | ||
ipData.List = append(ipData.List, ipItem) | ||
ipData.ListLength++ | ||
} | ||
|
||
} | ||
return ipData | ||
} | ||
|
||
// 匹配 IP 地址信息 | ||
func GetIpInfo(ip string) (res IpInfoItem) { | ||
ipData := LoadIpData() | ||
value, ok := ipData.Cache.Load(ip) | ||
if ok { | ||
return value.(IpInfoItem) | ||
} | ||
|
||
ipTarget := calcIP(ip) | ||
for _, item := range ipData.List { | ||
if ipTarget >= item.IpStart && ipTarget <= item.IpEnd { | ||
item.IP = ip | ||
res = item | ||
break | ||
} | ||
} | ||
|
||
ipData.Cache.Store(ip, res) | ||
return | ||
} | ||
|
||
func calcIP(ip string) int64 { | ||
x := strings.Split(ip, ".") | ||
if len(x) < 4 { | ||
return 0 | ||
} | ||
b0, _ := strconv.ParseInt(x[0], 10, 0) | ||
b1, _ := strconv.ParseInt(x[1], 10, 0) | ||
b2, _ := strconv.ParseInt(x[2], 10, 0) | ||
b3, _ := strconv.ParseInt(x[3], 10, 0) | ||
return b0*16777216 + b1*65536 + b2*256 + b3*1 | ||
} | ||
|
||
func CheckIP(ip string) bool { | ||
addr := strings.Trim(ip, " ") | ||
pattern := `^(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$` | ||
if match, _ := regexp.MatchString(pattern, addr); match { | ||
return true | ||
} | ||
return false | ||
} |
Oops, something went wrong.