基於純真ip庫以及openresty 模仿實現類似搜狐ip獲取區域的服務


最近搜狐的ip獲取區域的很不穩定,所以參考搜狐的模式基於openresty+純真ip庫+ golang rest 服務的模式,實現了一個類似的參考

相關說明

純真ip是一個免費的,准確度也比較高的離線ip地址查詢庫,當然是需要自己的解析方法,這個我直接使用了網上大家寫好的基於golang
的實現,同時因為基於文件查詢,所以需要cache,這個參考了 https://github.com/chetansurwade/geoip2-rest-api-golang的一個實現
geoip2-rest-api-golang 基於gin以及geoip2 實現查詢,但是因為geoip2 目前的下載以及對於國內的處理不是很好,所以使用了純真ip庫替換
對於cache 部分geoip2-rest-api-golang的實現基於社區的github.com/gin-contrib/cache 還是比較方便的

代碼說明

純真ip庫的處理基於https://github.com/freshcn/qqwry的一個實現,直接復用了加載以及查詢處理,整體就是一個代碼的聚合。。。。

  • main.go
    代碼入口,核心功能都在里邊,cache 默認是分鍾級別的,支持多ip 的查詢處理,目前純真ip庫使用了5.30號的(目前最新),對於其他
    更新自己處理下
 
package main
import (
  "net"
  "net/http"
  "time"
  "github.com/gin-contrib/cache"
  "github.com/gin-contrib/cache/persistence"
  "github.com/gin-gonic/gin"
)
// UserIP for user's ip info
type UserIP struct {
  UserIP []string `form:"ip" json:"ip" xml:"ip" binding:"required"`
}
// Cors for cross origin resource sharing in header
func Cors() gin.HandlerFunc {
  return func(c *gin.Context) {
    c.Writer.Header().Add("Access-Control-Allow-Origin", "*")
    if c.Request.Method != "OPTIONS" {
      c.Next()
    } else {
      c.Writer.Header().Add("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
      c.Writer.Header().Add("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
      c.Writer.Header().Add("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
      c.Writer.Header().Add("Content-Type", "application/json")
      c.AbortWithStatus(http.StatusOK)
    }
  }
}
// main function containing the routes and db initialization
func main() {
  // set gin to production mode
  gin.SetMode(gin.ReleaseMode)
  IPData.FilePath = "qqwry.dat"
  IPData.InitIPData()
  qqWry := NewQQwry()
  r := gin.Default()
  r.Use(Cors())
  r.GET("/", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
      "message": "API for GeoIP details",
      "status":  true,
    })
  })
  // for faster response an inmemory cache store of routes
  store := persistence.NewInMemoryStore(time.Second)
  // Supports any method GET/POST along with any content-type like json,form xml
  // change the time.Minute to your choice of duration
  // supports a single ip as input, for example http://localhost:8080/geoip?ip=YOUR-IP or http://localhost:8080/geoip?ip=YOUR-IP&ip=YOUR-IP2&ip=YOUR-IP3
  r.Any("/geoip", cache.CachePage(store, time.Minute, func(c *gin.Context) {
    var usrIP UserIP
    err := c.ShouldBind(&usrIP)
    if err != nil {
      c.JSON(http.StatusBadRequest, gin.H{
        "error":  "bad request",
        "status": false,
      })
      return
    }
    userIPs := usrIP.UserIP
    if len(userIPs) < 1 {
      c.JSON(http.StatusBadRequest, gin.H{
        "error":  "Kindly specify the ip or array of ips",
        "status": false,
      })
      return
    }
    var results []interface{}
    for _, userIP := range userIPs {
      data := make(map[string]interface{}, 0)
      data["ip"] = userIP
      ip := net.ParseIP(userIP)
      if ip == nil {
        data["error"] = true
        results = append(results, data)
        continue
      }
      cityRecord := qqWry.Find(userIP)
      if len(cityRecord.Country) > 0 {
        data["city"] = cityRecord.Country
        data["area"] = cityRecord.Area
        results = append(results, data)
      }
    }
    c.JSON(http.StatusOK, gin.H{
      "result": results,
      "status": true,
    })
    return
  }))
  r.Run(":8080")
  return
}
  • openresty 集成
    為了保持與搜狐ip接口的一直,使用了openresty 進行api 聚合處理,當然可以直接在代碼處理,但是這樣就不靈活了
    nginx 參考配置
    基本原理就是基於proxy 代理獲取ip 服務的golang服務,同時模仿搜狐cityjson接口的基於ngx.location.capture 的,就不用
    使用rest 請求類庫了,簡單了好多
 
worker_processes  1;
user root;  
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    lua_need_request_body on;
    gzip  on;
    resolver 127.0.0.11 ipv6=off;          
    real_ip_header     X-Forwarded-For;
    real_ip_recursive on;
    upstream  geoips {
       server app:8080 weight=20 max_fails=2 fail_timeout=30s;
    }
    server {
        listen       80;
        charset utf-8;
        default_type text/html;
        location / {
             default_type text/plain; 
             index index.html;
        }
        location /cityjson{
            default_type application/javascript; 
            content_by_lua_block {
               local headers=ngx.req.get_headers()
               local cjson = require("cjson")
               local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
               local res = ngx.location.capture('/geoip?ip='..ip)
               local info =[[var returnCitySN = {"cip":"]]..""..[[", "cid": "110000"]] ..[[, "cname":"]]..""..[["}; ]]
               if res.status==200 and res.body ~=nil  then
                 local ipadd = cjson.decode(res.body)
                 local cipinfo = {
                    cip = ipadd["result"][1].ip,
                    cname = ipadd["result"][1].city
                 }
                 info =[[var returnCitySN = {"cip":"]]..cipinfo.cip..[[", "cid": "110000"]] ..[[, "cname":"]]..cipinfo.cname..[["}; ]]
               end
               ngx.say(info)
            }
        }
        location /geoip {
            proxy_pass http://geoips;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

docker 鏡像

基於多階段構建

  • dockerfile
FROM golang:1.13-alpine AS build-env
WORKDIR /go/src/app
ENV  GO111MODULE=on
ENV  GOPROXY=https://goproxy.cn
COPY . .
RUN apk update && apk add git \
    && go build -o qqwry-rest
FROM alpine:latest
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY --from=build-env /go/src/app/qqwry-rest .
COPY qqwry.dat .
EXPOSE 8080
ENTRYPOINT [ "./qqwry-rest" ]

說明

以上是一個簡單的代碼集成,主要是不希望太依賴搜狐(還是不想花錢),而且很多時候有一個本地的兜底服務,還是比較好的
至少有可選的方案

參考資料

http://www.cz88.net/ip
https://pv.sohu.com/cityjson
https://github.com/WisdomFusion/qqwry.dat
https://github.com/freshcn/qqwry
https://github.com/chetansurwade/geoip2-rest-api-golang
https://github.com/rongfengliang/qqwry-rest-api


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM