客戶端灰度發佈,NGINX+GeoIP2+GeoIP Database

需求的產生 (分地區更新)

我們公司呢是做電商平臺的,其主打商品裏有一款智能飲水機產品,而每臺智能飲水機產品都是裝在全國客戶的家裏。爲了售後維護需要服務端如何保存數據並傳回這塊實現較簡單,而在遠程展示水機的餘額、歸屬地、出水的質量等功能這塊是由水機自身所鑲嵌的一塊智能PAD屏所完成,其PAD內部安裝的是安卓系統;
在由開發完成新功能的開發後需迭代智能PAD屏內部安卓系統APK版本時,在更新版本這塊我們一直做的方法是全量更新不做任何更新上的限制。但隨着業務量的增加全國大概有30萬臺水機版本需要更新,顯然之前的更新方式不再適用於現有這種高業務量的需求了。所以我們考慮了一個新的更新方案 "按地區更新";

參考的方案

一、最直接的方案是購買阿里雲的CDN,利用CDN的緩存來實現。緩存原理如下

  • 通過CDN訪問的流程是:客戶端-->CDN L1--->CDN L2--->源站;
  • 客戶端請求到的CDN節點是CDN的L1節點,當客戶端請求到CDN的L1節點向該節點請求一個資源的時候,該CDN節點會查詢本節點是否有這個資源,如有這個資源就會直接返回給客戶端。如果沒有這個資源,CDN L1節點就會向CDN的L2節點去請求資源,L2節點如果有緩存該文件,那麼L1向L2拿到數據以後就返回給客戶端。如果L2節點也沒有緩存該資源,那麼CDN的L2節點會回源向源站去請求這個資源然後返回給客戶端,並且根據具體的緩存規則把該資源緩存下來,具體緩存多久是根據緩存規則而定一旦緩存過期CDN節點就會清空該資源,等到下一次有客戶端請求到該節點的時候,CDN纔會回源去獲取數據。這裏一個L2節點是對應多個L1節點的,如果L1沒有命中緩存,L2命中了緩存,那麼最終也是命中緩存的;
  • 如果源站更新了APK文件,而CDN緩存還沒有過期的話,客戶可能會請求到老的APK數據。所以源站更新數據以後,需要到CDN上去刷新下緩存;
  • 後來考慮項目成本的問題就放棄了;

二、採用灰度發佈

  • 由於各項目的不互通性,而且灰度發佈是在代碼層實現的,不好應用到現在這個架構中;
  • 這種灰度的方式是根據版本號來迭代,即前端+CDN+Nginx的方式;
  • 討論過以後開發週期長,維護人員成本較高
  • 後來思路還是放在Nginx上,Nginx+GeoIP Modules+GeoIP Datebase;
  • 一點想法都沒有,該花的錢還是要花啊啊啊;

Nginx+GeoIP Modules+GeoIP Datebase

  • 編譯Nginx引入第三方模塊,這裏注意了需引入之前已編譯成功後的第三方模塊,不然你就完了...
  • 下載GeoIP庫 傳送門 ,解壓到/usr/share/GeoIP目錄下。此類資源在網內csdn還是有的比較有心;
  • 使用系統是CentOS7.2以上版本GeoIP包已安裝(rpm -ql GeoIP),但可惜的是它不再自動爲您更新庫;
  • 如編譯報錯 "the GeoIP module requires the GeoIP library",請 yum install -y geoip-devel
  • 編譯後禁止make install;
  • make 完成後把/root/nginx-1.14.2/objs/nginx 複製到nginx應用程序目錄下並覆蓋;
[root@node1-master nginx-1.14.2]# cd /usr/share/GeoIP
[root@node1-master GeoIP]# gzip -d GeoLiteCity.dat.gz
[root@node1-master GeoIP]# ln -sv GeoLiteCity.dat GeoCity.dat
[root@node1-master ~]# wget http://nginx.org/download/nginx-1.14.2.tar.gz 
[root@node1-master ~]# tar xf nginx-1.14.2.tar.gz && cd nginx-1.14.2 
[root@node1-master nginx-1.14.2]# nginx -V
[root@node1-master nginx-1.14.2]# ./configure \
--user=nginx \
--group=nginx  \
--with-http_stub_status_module \
--prefix=/usr/local/nginx \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--with-threads \
--with-http_ssl_module \
--with-pcre --with-pcre=/usr/local/nginx/modules/pcre-8.30 \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-stream --with-http_slice_module \
--with-cc-opt=-DTCP_FASTOPEN=23 \
--add-module=/usr/local/nginx/modules/ngx_cache_purge-2.3 \
--add-module=/usr/local/nginx/modules/nginx_upstream_check_module \
--add-module=/usr/local/nginx/modules/file-md5-master \
--add-module=/usr/local/nginx/modyles/nginx-sticky
--with-http_geoip_module  //此模塊即爲編譯時所加入的靜態geoip模塊
[root@node1-master nginx-1.14.2]# make
[root@node1-master nginx-1.14.2]# cp ./objs/nginx /usr/local/nginx/sbin/nginx
[root@node1-master nginx-1.14.2]# nginx -V
會顯示--with-http_geoip_module此模塊證明編譯成功

Nginx 配置文件參考

user root root;
worker_processes  auto;
worker_cpu_affinity auto;
error_log /usr/local/nginx/logs/error.log  crit;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65535;

events {
worker_connections 65535;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
access_log logs/access.log  main;

##定義GeoIP數據庫及存儲路徑## 
geoip_city /usr/share/GeoIP/GeoCity.dat;
geoip_proxy 192.168.0.0/24;
geoip_proxy 192.168.1.0/24;
geoip_proxy_recursive on;

##結合GeoIP數據庫並引入map指令來自定義變量,充分發揮作用##
map $geoip_city $no_allowed_region {
default 1;
Shanghai yes;
Beijing yes;
Tianjin yes;
}

server {
listen 80;
listen 443 default_server;
server_name you.domain.com;
ssl on;
ssl_certificate  /usr/local/nginx/ssl/you.domain.com.crt;
ssl_certificate_key  /usr/local/nginx/ssl/you.domain.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
root   /html;
index index.html index.htm;
try_files /index_$geoip_city.html ./index.html;

location /static {
root   /data;
    }
location ~ ^/api/pad/available {
default_type application/json;
return 200 '{"success":true}';
    }
#變量判斷#
location /files/pad/yimi-1.2.9.apk {
imit_conn addr 10;
limit_rate 100k;
if ($no_allowed_region = 1) {
return 550;
        }
    }
#變量判斷#
location /files/pad/yimipad-version.json {
limit_conn addr 10;
limit_rate 100k;
if ($no_allowed_region = 1) {
return 550;
        }
    }
location ~ ^/static/water/material {
root       /data/files;
limit_conn addr 10;
limit_rate 100k;
        }
    }
}
  • 匹配apk文件:
    當訪問的是/files/pad/yimi-1.2.9.apk文件時以下配置段被命中。if判斷讀取map指令中源變量$geoip_city的值去匹配後一個變量$no_allowed_region的值,如匹配成功則放行。其它或空則匹配默認規則,默認規則值是 default 1,如果匹配到默認規則的話在http響應狀態碼中則返回550作爲警示碼;
    location /files/pad/yimi-1.2.9.apk {
    root /data;
    limit_conn addr 10;
    limit_rate 100k;
    if ($no_allowed_region = 1) {
    return 550;
        }
    }
  • 匹配json文件:
    當訪問的是/files/pad/yimipad-version.json文件時以下配置段被命中。if判斷讀取map指令中源變量$geoip_city的值去匹配後一個變量$no_allowed_region的值,如匹配成功則放行。其它或空則匹配默認規則,默認規則值是 default 1,如果匹配到默認規則的話在http響應狀態碼中則返回550作爲警示碼;
    location /files/pad/yimipad-version.json {
    root /data;
    limit_conn addr 10;
    limit_rate 100k;
    if ($no_allowed_region = 1) {
    return 550;
        }
    }
    默認黑名單機制 default 1,即所有請求全部拒絕。只有在map配置段中添加 CITY = YES時規則纔有效;

從GeoIP升級到GeoIP2

  • 安裝開發包組
[root@node1-master ~]# yum groupinstall -y Development Tools
[root@node1-master ~]# yum install -y pcre-devel openssl openssl-devel zlib-devel
  • 下載編譯libmaxmindb依賴庫
official help site https://github.com/maxmind/libmaxminddb  
[root@node1-master ~]# cd /usr/local/src && git clone --recursive https://github.com/maxmind/libmaxminddb
[root@node1-master ~]# cd libmaxminddb
[root@node1-master ~]# ./bootstrap
[root@node1-master ~]# ./configure
[root@node1-master ~]# make
[root@node1-master ~]# make install
[root@node1-master ~]# sh -c "echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf"
[root@node1-master ~]# ldconfig
  • 平滑升級Nginx並編譯(ngx_http_geoip2_module)模塊
參考"Nginx+GeoIP Modules+GeoIP Datebase"編譯nginx的方法,一定要帶上原有參數
[root@node1-master ~]# nginx -V
[root@node1-master ~]# cd /usr/local/nginx/modules
[root@node1-master ~]# git clone --recursive https://github.com/leev/ngx_http_geoip2_module
[root@node1-master ~]# cd /root/nginx-1.14.2
[root@node1-master nginx-1.14.2]# ./configure --原有參數 --add-module=/usr/local/nginx/modules/ngx_http_geoip2_module
[root@node1-master nginx-1.14.2]# make 
[root@node1-master nginx-1.14.2]# mv /usr/local/nginx/sbin/nginx{,.baks}
[root@node1-master nginx-1.14.2]# cp objs/nginx /usr/local/nginx/sbin
[root@node1-master nginx-1.14.2]# make upgrade
如果在make upgrade的時候報錯 "make: *** [upgrade] error 1" 先完全kill掉nginx主進程,然後使用-c選項啓動nginx,再次執行make upgrade
[root@node1-master ~]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
這裏把ngx_http_geoip2_module構建爲靜態模塊,如果構建爲動態模塊需Nginx版本大於1.9.11+
  • 下載libmaxminddb數據庫文件
official help site https://dev.maxmind.com/geoip/geoip2/geolite2
[root@node1-master ~]# wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz
[root@node1-master ~]# wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
[root@node1-master ~]# tar xf GeoLite2-City.tar.gz -C /usr/share/GeoIP2
[root@node1-master ~]# tar xf GeoLite2-Country.tar.gz -C /usr/share/GeoIP2
  • 修改nginx配置替換GeoIP內容如下
##geoip2 with##
geoip2 /usr/share/GeoIP2/GeoLite2-City.mmdb {
auto_reload 60m;
$geoip2_metadata_city_build metadata build_epoch;
$geoip2_data_city_name city names en;
$geoip2_data_city_name default=Shanghai city names en;
}

##map geoip##
map $geoip2_data_city_name  $default_city_list {
default 1;
Shanghai yes;
}

##server or location##
if ($default_city_list = 1) {
        return 770;
}
  • 重載nginx配置文件 nginx -s reload

測試命令 mmdblookup

  • 測試在mmdb數據庫中查找數據路徑(國家/地區名稱en)
  • mmdblookup --file /usr/share/GeoIP2/GeoLite2-City.mmdb --ip 49.7.20.53
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章