golang 根据ip获取国家城市

准备做一个天气预报的模块,需要获取访问网站的用户所在的国家和城市。简单记录一下如何通过go语言实现。

目前主流的方法分为在线和离线两种,作为一项非常基础的功能,我个人倾向于使用离线的方案进行IP查询。在实践后,给大家推荐两个方案:

  1. 基于GeoLite2-City.mmdb:MaxMind 公司开发的免费 IP 地理位置数据库,可以查询全球所有国家,其中对于美国地区的IP,可以定位到州市。
  2. 基于ip2region.xdb:GitHub开源者lionsoul2014狮子的魂开源的作品,可以查询全球所有国家,其中对于中国和美国地区的IP,可以精准到省/州市。

下面将用最简明扼要且优雅的命令来演示如何使用go语言使用这两个数据库查询IP的国家与城市。


GeoLite2-City.mmdb

IP与地区的映射表可以通过mmdb数据库获取,GeoLite2-City.mmdb下载方式:

git clone https://github.com/wp-statistics/GeoLite2-City.git

解压其中的压缩包获取GeoLite2-City.mmdb,这个文件含有全球的IP与区域的映射关系。

下载go语言解析与驱动mmdb的库geoip2

go get github.com/oschwald/geoip2-golang

下载完成后,使用如下代码来解析IP为128.101.101.101的目标所在国家和地区:

package main

import (
    "fmt"
    "net"
    "github.com/oschwald/geoip2-golang"
)

func main() {
    db, err := geoip2.Open("GeoLite2-City.mmdb")
    if err != nil {
        fmt.Println(err.Error())
    }
    defer db.Close()

    ip := net.ParseIP("128.101.101.101")
    record, err := db.City(ip)
    if err != nil {
        fmt.Println(err.Error())
    }

    fmt.Println(record.Country.Names)
    fmt.Println(record.Subdivisions[0].Names)
    fmt.Println(record.City.Names)
}

运行代码,得到结果:

map[de:Vereinigte Staaten en:United States es:Estados Unidos fr:États Unis ja:アメリカ pt-BR:EUA ru:США zh-CN:美国]
map[en:Minnesota es:Minnesota fr:Minnesota ja:ミネソタ州 pt-BR:Minesota ru:Миннесота zh-CN:明尼苏达州]
map[de:Minneapolis en:Minneapolis es:Mineápolis fr:Minneapolis ja:ミネアポリス pt-BR:Minneapolis ru:Миннеаполис zh-CN:明尼阿波利斯]

可以看到,geoip2的结果提供了不同的语言,各取所需即可。

但是对于中国的IP无法定位到省。


ip2region.xdb

先下载最新的优化数据库:

wget -o ip2region.xdb https://github.com/lionsoul2014/ip2region/raw/master/data/ip2region.xdb

下载go语言解析与驱动xdb的库xdb

go get github.com/lionsoul2014/ip2region/binding/golang/xdb

使用如下代码进行查询,并让数据库常驻内存(数据库很小,只有10MB).

package main

import (
    "fmt"
    "github.com/lionsoul2014/ip2region/binding/golang/xdb"
    "time"
)

var (
    dbPath string = "./ip2region.xdb"
    cBuff []byte
)

func init() {
    var err error
    cBuff, err = xdb.LoadContentFromFile(dbPath)
    if err != nil {
        fmt.Printf("failed to load content from `%s`: %s\n", dbPath, err)
        return
    }
}

func main() {
    searcher, err := xdb.NewWithBuffer(cBuff)
    if err != nil {
        fmt.Printf("failed to create searcher: %s\n", err.Error())
        return
    }

    defer searcher.Close()


    var ip = "39.156.66.10"
    var tStart = time.Now()
    region, err := searcher.SearchByStr(ip)
    if err != nil {
        fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
        return
    }

    fmt.Printf("region: %s, took: %s\n", region, time.Since(tStart))
}

运行代码,得到结果:

region: 中国|0|北京|北京市|移动, took: 0s

别忘了Nginx

为了正确地在服务器中获取真实IP,需要配置一下Nginx对于后端服务路由的配置,添加如下的配置:

location /api {
        proxy_set_header X-Forwarded-Proto  $scheme;
        proxy_set_header X-Forward-For $remote_addr;
        proxy_set_header X-real-ip $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

自从v1.7.2后,go的gin框架在获取用户真实IP方面有一点bug:https://github.com/gin-gonic/gin/issues/2697

所以我们尝试直接从请求头中获取真实IP:

func GetRealIP(c *gin.Context) string {
    return c.Request.Header.Get("X-Forward-For")
}

将这个返回给上方的函数即可正确获取请求用户的城市。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值