https://github.com/oschwald/geoip2-golang用来解析
[GeoLite2](http://dev.maxmind.com/geoip/geoip2/geolite2/)
and [GeoIP2](http://www.maxmind.com/en/geolocation_landing)数据库的一个工具包。类似于nginx的https://github.com/leev/ngx_http_geoip2_module
GeoIP2数据库有什么用呢?我们可以根据ip来获取ip的地理位置信息然后做响应的地域相关的业务:
1,简单的cdn,根据ip的地理信息重定向到合适的cdn服务器
2,做固定区域的业务屏蔽,比如:不给日本的用户提供服务
3,做国际化,根据不同的地域提供不同语言的服务。
比如我们常用的网络工具https://github.com/zu1k/nali 其实就用到了geoip2-golang这个包来解析GeoIP2数据。下面,我们看下这个包应该如何使用:
package mainimport ("fmt""log""net""github.com/oschwald/geoip2-golang")func main() {db, err := geoip2.Open("./GeoLite2-City.mmdb")if err != nil {log.Fatal(err)}defer db.Close()If you are using strings that may be invalid, check that ip is not nilip := net.ParseIP("180.101.49.12") 120.24.37.249record, err := db.City(ip)if err != nil {log.Fatal(err)}fmt.Printf("Portuguese (BR) city name: %v\n", record.City.Names["zh-CN"])fmt.Printf("English subdivision name: %v\n", record.Subdivisions[0].Names["en"])fmt.Printf("Russian country name: %v\n", record.Country.Names["en"])fmt.Printf("ISO country code: %v\n", record.Country.IsoCode)fmt.Printf("Time zone: %v\n", record.Location.TimeZone)fmt.Printf("Coordinates: %v, %v\n", record.Location.Latitude, record.Location.Longitude)/ Output:// Portuguese (BR) city name: Londres// English subdivision name: England// Russian country name: Великобритания// ISO country code: GB// Time zone: Europe/London// Coordinates: 51.5142, -0.0931}
非常简单,加载GeoLite2-City.mmdb数据库,解析ip地址,通过ip地址加载城市信息。
接着,我们详细分析下geoip2-golang这个包的源码。它的源码很简单只有一个文件:
reader.go
调用了maxminddb数据解析包github.com/oschwald/maxminddb-golang来做 数据的解析,仅仅做了一层接口上的封装,和对应地理数据格式(企业、城市、国家、AnonymousIP、Domain、ISP)的定义。
比如城市信息:
// The City struct corresponds to the data in the GeoIP2/GeoLite2 City// databases.type City struct {City struct {GeoNameID uint `maxminddb:"geoname_id"`Names map[string]string `maxminddb:"names"`} `maxminddb:"city"`Continent struct {Code string `maxminddb:"code"`GeoNameID uint `maxminddb:"geoname_id"`Names map[string]string `maxminddb:"names"`} `maxminddb:"continent"`Country struct {GeoNameID uint `maxminddb:"geoname_id"`IsInEuropeanUnion bool `maxminddb:"is_in_european_union"`IsoCode string `maxminddb:"iso_code"`Names map[string]string `maxminddb:"names"`} `maxminddb:"country"`Location struct {AccuracyRadius uint16 `maxminddb:"accuracy_radius"`Latitude float64 `maxminddb:"latitude"`Longitude float64 `maxminddb:"longitude"`MetroCode uint `maxminddb:"metro_code"`TimeZone string `maxminddb:"time_zone"`} `maxminddb:"location"`Postal struct {Code string `maxminddb:"code"`} `maxminddb:"postal"`RegisteredCountry struct {GeoNameID uint `maxminddb:"geoname_id"`IsInEuropeanUnion bool `maxminddb:"is_in_european_union"`IsoCode string `maxminddb:"iso_code"`Names map[string]string `maxminddb:"names"`} `maxminddb:"registered_country"`RepresentedCountry struct {GeoNameID uint `maxminddb:"geoname_id"`IsInEuropeanUnion bool `maxminddb:"is_in_european_union"`IsoCode string `maxminddb:"iso_code"`Names map[string]string `maxminddb:"names"`Type string `maxminddb:"type"`} `maxminddb:"represented_country"`Subdivisions []struct {GeoNameID uint `maxminddb:"geoname_id"`IsoCode string `maxminddb:"iso_code"`Names map[string]string `maxminddb:"names"`} `maxminddb:"subdivisions"`Traits struct {IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"`IsSatelliteProvider bool `maxminddb:"is_satellite_provider"`} `maxminddb:"traits"`}
先看下open函数
func Open(file string) (*Reader, error) {reader, err := maxminddb.Open(file)if err != nil {return nil, err}dbType, err := getDBType(reader)return &Reader{reader, dbType}, err}
它调用了 maxminddb.Open返回了一个Reader
type Reader struct {mmdbReader *maxminddb.ReaderdatabaseType databaseType}
Reader上定义了City,County等函数
func (r *Reader) City(ipAddress net.IP) (*City, error) {if isCity&r.databaseType == 0 {return nil, InvalidMethodError{"City", r.Metadata().DatabaseType}}var city Cityerr := r.mmdbReader.Lookup(ipAddress, &city)return &city, err}
这些函数也只是对mmdbReader.Lookup做了简单的封装。






