基於Golang的IP地址資訊查詢服務

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

原文連結:http://tabalt.net/blog/ipquer...

工作中經常會有通過IP匹配使用者資訊的需求,如確定使用者所在的地區(國家/省份/城市)、電訊廠商、時區、經緯度等等。前一陣有個Golang開發的項目也有這樣的需求,於是簡單實現了一個包,最近忙裡偷閒又包了一個支援HTTP和GRPC方式調用的服務,並開源在GitHub上了。本文主要介紹IP地址資訊查詢的實現細節和使用方式。

首先交代一下GitHub地址:

  • IpQuery Golang Package:https://github.com/tabalt/ipquery

  • IP地址資訊查詢服務:https://github.com/tabalt/ipqueryd

歡迎大家在項目中使用(已通過N億日PV服務的考驗),有任何問題或建議,請提交Issue反饋或Fork到自己名下修改後提交Pull Request。

IP資料檔案

IP資料檔案存放IP位址區段和資料資訊的映射關係,是IP地址資訊查詢中最重要的部分,格式上要求可擴充,資料上則需要準確甚至精確。真正意義上完美的IP資料檔案是不存在的,而要想讓資料檔案保持可用,需要定期對資料檔案做維護更新。

資料格式

IP資料檔案通常是純文字形式的,映射關係按行存放,每行的各項之間用"t"分隔,前面兩列是IP段的起始和結束點轉換成無符號32位整型的值(只考慮IPV4),後面的部分則是各項資訊,可根據實際需要擴充;各行之間要求是按IP段從小到大升序排列的。樣本格式如下:

1033224192    1033228287    北京    北京    朝陽    聯通1033228288    1033232383    北京    北京    海澱    聯通1033232384    1033233407    北京    北京    昌平    聯通

資料來源

IP資訊資料主要有3個來源:

  • 具有一定規模的公司大都會自己維護一份IP庫,如果你在這些公司工作,可以直接使用

  • 網路上有一些免費的IP庫(如純真IP庫)

  • 購買商業的IP庫(如IPIP.NET)

此外也能通過一定的技術或人工手段自行擷取維護IP資訊資料庫,但是成本會非常高。

IP地址資訊查詢的原理

有了資料檔案,要實現資訊查詢並不難,簡單方式是直接將資料檔案載入到記憶體數組中,尋找時將IP地址轉換成無符號32位整型,然後用二分尋找法尋找整型所在的區間,找到後則返回對應的資料,沒找到則返回失敗。Golang中核心代碼如下:

將IP地址字串轉成無符號32位整型:

func ip2Long(ip string) uint32 {    var long uint32    binary.Read(bytes.NewBuffer(net.ParseIP(ip).To4()), binary.BigEndian, &long)    return long}

主要結構體:

type IpRange struct {    Begin uint32    End   uint32    Data  []byte}type IpData []*IpRange

二分尋找:

func (id *IpData) getIpRange(ip string) (*IpRange, error) {    var low, high int = 0, (id.Length() - 1)    ipdt := *id    il := ip2Long(ip)    if il <= 0 {        return nil, ErrorIpRangeNotFound    }    for low <= high {        var middle int = (high-low)/2 + low        ir := ipdt[middle]        if il >= ir.Begin && il <= ir.End {            return ir, nil        } else if il < ir.Begin {            high = middle - 1        } else {            low = middle + 1        }    }    return nil, ErrorIpRangeNotFound}

Golang ipquery包介紹

ipquery包的用法

ipquery包(https://github.com/tabalt/ipq...)用起來很簡單,匯入包後通過ipquery.Load()方法初始化載入IP資料檔案,然後就可以使用ipquery.Find()方法來查詢IP地址對應的資訊了。範例程式碼如下:

package mainimport (    "fmt"    "github.com/tabalt/ipquery")func main() {    df := "testdata/test_10000.data"    err := ipquery.Load(df)    if err != nil {        fmt.Println(err)    }    ip := "61.149.208.1"    dt, err := ipquery.Find(ip)    if err != nil {        fmt.Println(err)    } else {        fmt.Println(ip, string(dt))    }}

如果你想在程式運行過程中安全地更新資料檔案,請使用ipquery.ReLoad()方法;ipquery.Length()則可以擷取到載入到記憶體的資料總條數。

上面介紹的方法其實都是為了方便使用而封裝的快捷方法,也可以直接使用ipquery.NewIpData()方法返回的IpData結構體獲得更大的靈活性。如給IpData結構體的Load或ReLoad方法傳入一個自訂的io.Reader可以從非文字檔的資料來源初始化ipquery包。

ipquery包的壓測結果

ipquery包提供了較完善的單元測試,複製代碼到GOPATH中後,進入$GOPATH/ipqeury目錄,執行go test相關命令即可執行測試代碼:

[tabalt@localhost ipquery] go test -v=== RUN   TestIpData_Load--- PASS: TestIpData_Load (0.01s)=== RUN   TestIpData_Find--- PASS: TestIpData_Find (0.01s)=== RUN   TestIpData_Parallel_Find--- PASS: TestIpData_Parallel_Find (0.01s)PASSok      ipquery 0.051s

從壓測結果上看ipquery包的效能是相當不錯的,在一台2核4G CentOS 6.2 Golang 1.7.1虛擬機器開發機上,初始化23M的資料檔案平均耗時500ms左右,執行尋找平均耗時0.012ms,具體資料如下:

[tabalt@localhost ipquery] go test -bench=.BenchmarkIpData_Load-2                 3         452223279 ns/op        97439626 B/op    1780052 allocs/opBenchmarkIpData_Find-2            100000             11472 ns/op            1118 B/op         21 allocs/opPASSok      ipquery 33.488s[tabalt@localhost ipquery] go test -bench=.BenchmarkIpData_Load-2                 3         500309108 ns/op        97439621 B/op    1780052 allocs/opBenchmarkIpData_Find-2            100000             11809 ns/op            1118 B/op         21 allocs/opPASSok      ipquery 33.498s[tabalt@localhost ipquery] go test -bench=.BenchmarkIpData_Load-2                 3         436756760 ns/op        97439621 B/op    1780052 allocs/opBenchmarkIpData_Find-2            100000             12574 ns/op            1118 B/op         21 allocs/opPASSok      ipquery 34.510s

IP地址資訊查詢服務介紹

如文章開頭所說,這個項目基於ipquery包提供HTTP和GRPC介面,名字也就很俗的取為ipqueryd。個人習慣項目級的Go代碼不放在全域的GOPATH裡,而是使用shell指令碼來動態修改GOPATH為項目目錄後執行go命令,因此可以使用如下步驟運行本項目:

[tabalt@localhost ~] git clone https://github.com/tabalt/ipqueryd.git ~/$NOT_YOUR_GOPATH/[tabalt@localhost ipqueryd] cd ~/$NOT_YOUR_GOPATH/ipqueryd[tabalt@localhost ipqueryd] ./ctrl.sh run

設定檔

項目中conf目錄下有個ipqueryd.json的設定檔,可以配置PID檔案、HTTP服務連接埠、GRPC服務連接埠、資料檔案路徑等內容,可以根據需求修改;服務連接埠可以只配其中一個也可以兩個都配上。

{    "pid_file": "./tmp/ipqueryd.pid",    "http_server_port": ":12101",    "grpc_server_port": ":12102",    "data_file": "./data/ip_data.txt"}

HTTP介面

HTTP介面支援返回JSON格式和JSONP格式的響應,下面使用命令列測試:

[tabalt@localhost ipqueryd] curl "http://127.0.0.1:12101/find?ip=1.1.8.1"{"data":["廣東省電信"]}[tabalt@localhost ipqueryd] curl "http://127.0.0.1:12101/find?ip=1.1.8.1&_callback=showip"showip({"data":["廣東省電信"]});

GRPC介面

GRPC介面需要使用以你熟悉的語言編寫用戶端,下面的代碼是Golang中的簡單使用:

package mainimport (    "log"    "golang.org/x/net/context"    "google.golang.org/grpc"    "pb")func main() {    conn, err := grpc.Dial("127.0.0.1:12102", grpc.WithInsecure())    if err != nil {        log.Fatalf("did not connect: %v", err)    }    defer conn.Close()    iqc := pb.NewIpQueryClient(conn)    ip := "1.1.8.1"    r, err := iqc.Find(context.Background(), &pb.IpFindRequest{Ip: ip})    if err != nil {        log.Fatalf("could not find: %v", err)    }    log.Printf("ip data: %s", r.Data)}

更多內容等你來發現和貢獻!

如果文章對您有協助,歡迎打賞, 您的支援是我碼字的動力!

原文連結:http://tabalt.net/blog/ipquer...

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.