准备做一个天气预报的模块,需要获取访问网站的用户所在的国家和城市。简单记录一下如何通过go语言实现。
目前主流的方法分为在线和离线两种,作为一项非常基础的功能,我个人倾向于使用离线的方案进行IP查询。在实践后,给大家推荐两个方案:
- 基于
GeoLite2-City.mmdb:MaxMind 公司开发的免费 IP 地理位置数据库,可以查询全球所有国家,其中对于美国地区的IP,可以定位到州市。 - 基于
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")
}
将这个返回给上方的函数即可正确获取请求用户的城市。
5万+

被折叠的 条评论
为什么被折叠?



