Openresty + GeoIP2實現IP歸屬地查詢

爲了實現業務系統針對不同地區IP訪問,展示包含不同地區信息的業務交互界面。很多情況下系統需要根據用戶訪問的IP信息,判斷用戶可能的訪問區域,針對不同的區域提供個性化的服務內容。本方案在CentOS7.x環境下基於高性能的Openresty1.13.6.2來實現。

方案介紹

要通過IP地址確認歸屬地,通常可以使用一些在線查詢服務來實現,比如https://blog.csdn.net/XinTeng2012/article/details/34418117?utm_source=blogxgwz8 介紹的常見提供查詢服務。 但使用在線服務查詢潛在存在性能問題,同時通過lua來訪問外部服務增加額外的代碼量。 通過本地的GeoIP庫來實現查詢是個比較好的方案,GeoIP提供免費和收費服務(https://www.maxmind.com/en/home),大多數情況下使用定期更新的GeoIP數據庫能滿足基本需求。

因此,可以在openresty中通過lua庫本地GeopIP數據庫的方式來實現快速位置查詢和用戶訪問界面重定向。

 

環境準備

一:OpenResty安裝

OpenResty方便地將Nginx和常用的各類lua庫打包發佈,可以方便地參考 https://openresty.org/en/installation.html 文檔從源碼編譯安裝。主要安裝步驟說明如下:

tar -xvf openresty-VERSION.tar.gz
cd openresty-VERSION/
./configure -j2 --prefix=/usr/local/openresty
make -j2
sudo make install

# better also add the following line to your ~/.bashrc or ~/.bash_profile file.
export PATH=/usr/local/openresty/bin:$PATH

這裏的VERSION 是OpenResty具體版本號,目前爲 1.13.6.2。編譯安裝後可以通過如下命令查看版本信息:

/usr/local/openresty/bin/openresty -V
nginx version: openresty/1.13.6.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)?
built with OpenSSL 1.0.2k-fips ?26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.13 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.15 --add-module=../rds-csv-nginx-module-0.09 --add-module=../ngx_stream_lua-0.0.5 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --with-stream --with-stream_ssl_module --with-http_ssl_module

openresty包含了自身的包維護工具opm,該工具採用perl實現依賴MD5,需要執行yum install  -y perl-Digest-MD5 安裝。

二:GeoIP2安裝

https://dev.maxmind.com/geoip/geoip2/geolite2/ 下載MaxMind格式的GeoIP2數據庫保存到本地服務器。比如將數據庫文件GeoLite2-City.mmdb保存到/usr/local/openresty目錄下。

GeoIP2 lua庫安裝,GeoIP2 lua庫位於https://github.com/anjia0532/lua-resty-maxminddb ,可以通過如下命令方便安裝:

/usr/local/openresty/bin/opm get anjia0532/lua-resty-maxminddb

GeoIP2 lua庫依賴動態庫安裝,lua庫依賴libmaxminddb實現對mmdb的高效訪問。需要編譯該庫並添加到openresty訪問環境。可以從https://github.com/maxmind/libmaxminddb/releases下載相應源碼包到本地編譯部署。基本編譯步驟如下:

$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig

默認情況下上述操作會將libmaxminddb.so部署到/usr/local/lib目錄下,爲了讓openresty訪問,可以拷貝到openresty目錄下,或通過如下步驟更新ldconfig。

$ sudo sh -c "echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf"
$ ldconfig

三:配置openresty nginx環境。

1,配置openresty nginx加載相應的lua庫和動態庫,需要在http段添加如下指令,其中的;;表示默認庫路徑:

        lua_package_path  "/usr/local/openresty/lualib/?.lua;;";
        lua_package_cpath  "/usr/local/openresty/lualib/?.so;;";

2,指定lua處理請求的方式。 爲了簡易直觀,如下示例的nginx.conf配置指定 /lua開始的url請求通過conf/lua/test.lua腳本來處理,這裏沒有做其他複雜的請求和變量處理工作。 現網環境下可能需要考慮更好的模塊化配置管理方式,lua_code_cache off;參數只爲測試使用,生產環境需設爲on;

http {
        lua_package_path  "/usr/local/openresty/lualib/?.lua;;";
        lua_package_cpath  "/usr/local/openresty/lualib/?.so;;";

    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }
        location /lua {
                default_type "text/html";
                charset utf-8;
                lua_code_cache off;
                content_by_lua_file  conf/lua/test.lua;
        }
    }

四:編寫訪問mmdb的lua腳本

我們想通過傳入IP地址的方式來查看歸屬地城市,比如,http://192.168.137.70/lua?ip=111.47.224.182&node=city。 可以如下方式編寫按照上面設置的conf/lua/test.lua。

ngx.say("<br>IP location query result:<hr><br>")

local cjson=require 'cjson'
local geo=require 'resty.maxminddb'
local arg_ip=ngx.var.arg_ip
local arg_node=ngx.var.arg_node
ngx.say("IP:",arg_ip,", node:",arg_node,"<br>")

if not geo.initted() then
        geo.init("/usr/local/openresty/GeoLite2-City.mmdb")
end



local res,err=geo.lookup(arg_ip or ngx.var.remote_addr)

if not res then
        ngx.say("Please check the ip address you provided: <div style='color:red'>",arg_ip,"</div>")
        ngx.log(ngx.ERR,' failed to lookup by ip , reason :',err)
else
        ngx.say("Result:",cjson.encode(res))

        if arg_node then
                ngx.say("node name:",ngx.var.arg_node, " , value:",cjson.encode(res[ngx.var.arg_node] or {}))

        end

end

五:訪問驗證

curl -k -L "http://192.168.137.70/lua?ip=111.47.224.182&node=country"
<br>IP location query result:<hr><br>
IP:111.47.224.182, Node:country<br>
Result:{"city":{"geoname_id":1791247,"names":{"en":"Wuhan","ru":"Ухань","fr":"Wuhan","pt-BR":"Wuhan","zh-CN":"武漢","es":"Wuhan","de":"Wuhan","ja":"武漢市"}},"subdivisions":[{"geoname_id":1806949,"names":{"en":"Hubei","ru":"Хубэй","fr":"Province de Hubei","pt-BR":"Hubei","zh-CN":"湖北省","es":"Hubei","de":"Hubei","ja":"湖北省"},"iso_code":"HB"}],"country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中國","es":"China","de":"China","ja":"中國"},"iso_code":"CN"},"registered_country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中國","es":"China","de":"China","ja":"中國"},"iso_code":"CN"},"location":{"time_zone":"Asia\/Shanghai","longitude":114.2734,"accuracy_radius":200,"latitude":30.5801},"continent":{"geoname_id":6255147,"names":{"en":"Asia","ru":"Азия","fr":"Asie","pt-BR":"Ásia","zh-CN":"亞洲","es":"Asia","de":"Asien","ja":"アジア"},"code":"AS"}}
<br>Node name:country<br>Value:{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"中國","es":"China","de":"China","ja":"中國"},"iso_code":"CN"}
"武漢"

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章