UDN-企业互联网技术人气社区

板块导航

浏览  : 1707
回复  : 0

[干货] 基于Golang的IP地址信息查询服务

[复制链接]
开花包的头像 楼主
发表于 2016-12-31 22:21:24 | 显示全部楼层 |阅读模式
  工作中经常会有通过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段从小到大升序排列的。示例格式如下:

  
  1. 1033224192 1033228287 北京 北京 朝阳 联通

  2.   1033228288 1033232383 北京 北京 海淀 联通

  3.   1033232384 1033233407 北京 北京 昌平 联通
复制代码


  数据来源

  IP信息数据主要有3个来源:

  具有一定规模的公司大都会自己维护一份IP库,如果你在这些公司工作,可以直接使用

  网络上有一些免费的IP库(如纯真IP库)

  购买商业的IP库(如IPIP.NET)

  此外也能通过一定的技术或人工手段自行获取维护IP信息数据库,但是成本会非常高。

  IP地址信息查询的原理

  有了数据文件,要实现信息查询并不难,简单方式是直接将数据文件加载到内存数组中,查找时将IP地址转换成无符号32位整型,然后用二分查找法查找整型所在的区间,找到后则返回对应的数据,没找到则返回失败。Golang中核心代码如下:

  将IP地址字符串转成无符号32位整型:

  1.   func ip2Long(ip string) uint32 {

  2.   var long uint32

  3.   binary.Read(bytes.NewBuffer(net.ParseIP(ip).To4()), binary.BigEndian, &long)

  4.   return long

  5.   }
复制代码


  主要结构体:

  1.   type IpRange struct {

  2.   Begin uint32

  3.   End uint32

  4.   Data []byte

  5.   }

  6.   type IpData []*IpRange
复制代码


  二分查找:

  1.   func (id *IpData) getIpRange(ip string) (*IpRange, error) {

  2.   var low, high int = 0, (id.Length() - 1)

  3.   ipdt := *id

  4.   il := ip2Long(ip)

  5.   if il <= 0 {

  6.   return nil, ErrorIpRangeNotFound

  7.   }

  8.   for low <= high {

  9.   var middle int = (high-low)/2 + low

  10.   ir := ipdt[middle]

  11.   if il >= ir.Begin && il <= ir.End {

  12.   return ir, nil

  13.   } else if il < ir.Begin {

  14.   high = middle - 1

  15.   } else {

  16.   low = middle + 1

  17.   }

  18.   }

  19.   return nil, ErrorIpRangeNotFound

  20.   }
复制代码


  Golang ipquery包介绍

  ipquery包的用法

  ipquery包(https://github.com/tabalt/ipquery/)用起来很简单,导入包后通过ipquery.Load()方法初始化加载IP数据文件,然后就可以使用ipquery.Find()方法来查询IP地址对应的信息了。示例代码如下:

  1.   package main

  2.   import (

  3.   "fmt"

  4.   "github.com/tabalt/ipquery"

  5.   )

  6.   func main() {

  7.   df := "testdata/test_10000.data"

  8.   err := ipquery.Load(df)

  9.   if err != nil {

  10.   fmt.Println(err)

  11.   }

  12.   ip := "61.149.208.1"

  13.   dt, err := ipquery.Find(ip)

  14.   if err != nil {

  15.   fmt.Println(err)

  16.   } else {

  17.   fmt.Println(ip, string(dt))

  18.   }

  19.   }
复制代码


  如果你想在程序运行过程中安全地更新数据文件,请使用ipquery.ReLoad()方法;ipquery.Length()则可以获取到加载到内存的数据总条数。

  上面介绍的方法其实都是为了方便使用而包装的快捷方法,也可以直接使用ipquery.NewIpData()方法返回的IpData结构体获得更大的灵活性。如给IpData结构体的Load或ReLoad方法传入一个自定义的io.Reader可以从非文本文件的数据源初始化ipquery包。

  ipquery包的压测结果

  ipquery包提供了较完善的单元测试,克隆代码到GOPATH中后,进入$GOPATH/ipqeury目录,执行go test相关命令即可执行测试代码:

  1.   [tabalt@localhost ipquery] go test -v

  2.   === RUN TestIpData_Load

  3.   --- PASS: TestIpData_Load (0.01s)

  4.   === RUN TestIpData_Find

  5.   --- PASS: TestIpData_Find (0.01s)

  6.   === RUN TestIpData_Parallel_Find

  7.   --- PASS: TestIpData_Parallel_Find (0.01s)

  8.   PASS

  9.   ok ipquery 0.051s
复制代码


  从压测结果上看ipquery包的性能是相当不错的,在一台2核4G CentOS 6.2 Golang 1.7.1虚拟机开发机上,初始化23M的数据文件平均耗时500ms左右,执行查找平均耗时0.012ms,具体数据如下:

  1.   [tabalt@localhost ipquery] go test -bench=.

  2.   BenchmarkIpData_Load-2 3 452223279 ns/op 97439626 B/op 1780052 allocs/op

  3.   BenchmarkIpData_Find-2 100000 11472 ns/op 1118 B/op 21 allocs/op

  4.   PASS

  5.   ok ipquery 33.488s

  6.   [tabalt@localhost ipquery] go test -bench=.

  7.   BenchmarkIpData_Load-2 3 500309108 ns/op 97439621 B/op 1780052 allocs/op

  8.   BenchmarkIpData_Find-2 100000 11809 ns/op 1118 B/op 21 allocs/op

  9.   PASS

  10.   ok ipquery 33.498s

  11.   [tabalt@localhost ipquery] go test -bench=.

  12.   BenchmarkIpData_Load-2 3 436756760 ns/op 97439621 B/op 1780052 allocs/op

  13.   BenchmarkIpData_Find-2 100000 12574 ns/op 1118 B/op 21 allocs/op

  14.   PASS

  15.   ok ipquery 34.510s
复制代码

  IP地址信息查询服务介绍

  如文章开头所说,这个项目基于ipquery包提供HTTP和GRPC接口,名字也就很俗的取为ipqueryd。个人习惯项目级的Go代码不放在全局的GOPATH里,而是使用shell脚本来动态修改GOPATH为项目目录后执行go命令,因此可以使用如下步骤运行本项目:

  1.   [tabalt@localhost ~] git clone https://github.com/tabalt/ipqueryd.git ~/$NOT_YOUR_GOPATH/

  2.   [tabalt@localhost ipqueryd] cd ~/$NOT_YOUR_GOPATH/ipqueryd

  3.   [tabalt@localhost ipqueryd] ./ctrl.sh run
复制代码


  配置文件

  项目中conf目录下有个ipqueryd.json的配置文件,可以配置PID文件、HTTP服务端口、GRPC服务端口、数据文件路径等内容,可以根据需求修改;服务端口可以只配其中一个也可以两个都配上。

  1.   {

  2.   "pid_file": "./tmp/ipqueryd.pid",

  3.   "http_server_port": ":12101",

  4.   "grpc_server_port": ":12102",

  5.   "data_file": "./data/ip_data.txt"

  6.   }
复制代码


  HTTP接口

  HTTP接口支持返回JSON格式和JSONP格式的响应,下面使用命令行测试:

  1.   [tabalt@localhost ipqueryd] curl "http://127.0.0.1:12101/find?ip=1.1.8.1"

  2.   {"data":["广东省电信"]}

  3.   [tabalt@localhost ipqueryd] curl "http://127.0.0.1:12101/find?ip=1.1.8.1&_callback=showip"

  4.   showip({"data":["广东省电信"]});
复制代码


  GRPC接口

  GRPC接口需要使用以你熟悉的语言编写客户端,下面的代码是Golang中的简单使用:

  1.   package main

  2.   import (

  3.   "log"

  4.   "golang.org/x/net/context"

  5.   "google.golang.org/grpc"

  6.   "pb"

  7.   )

  8.   func main() {

  9.   conn, err := grpc.Dial("127.0.0.1:12102", grpc.WithInsecure())

  10.   if err != nil {

  11.   log.Fatalf("did not connect: %v", err)

  12.   }

  13.   defer conn.Close()

  14.   iqc := pb.NewIpQueryClient(conn)

  15.   ip := "1.1.8.1"

  16.   r, err := iqc.Find(context.Background(), &pb.IpFindRequest{Ip: ip})

  17.   if err != nil {

  18.   log.Fatalf("could not find: %v", err)

  19.   }

  20.   log.Printf("ip data: %s", r.Data)

  21.   }
复制代码


原文作者:佚名  来源:开发者头条

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部