Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nuominmin committed Mar 26, 2024
0 parents commit d890024
Show file tree
Hide file tree
Showing 16 changed files with 1,864 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .gitignore
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
140 changes: 140 additions & 0 deletions README.md
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)
```
20 changes: 20 additions & 0 deletions cmd/main.go
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 {}
}
71 changes: 71 additions & 0 deletions config/loadYamlStruct.go
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
}
11 changes: 11 additions & 0 deletions core/calcIp_test.go
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)
}
126 changes: 126 additions & 0 deletions core/ipData.go
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
}
Loading

0 comments on commit d890024

Please sign in to comment.