通過Cookie Insertion實(shí)現(xiàn)Nginx會(huì)話保持

Nginx負(fù)載均衡的幾種策略

使用開源版本的Nginx可以輕松實(shí)現(xiàn)7層(HTTP)或4層(TCP)的負(fù)載均衡。

當(dāng)做為7層負(fù)載時(shí),最常用的有以下幾種策略:

  • 輪詢 round-robin — requests to the application servers are distributed in a round-robin fashion 依次把客戶端的請求分發(fā)到不同的后端服務(wù)器上
  • 最少連接 least-connected — next request is assigned to the server with the least number of active connections 請求會(huì)被轉(zhuǎn)發(fā)到連接數(shù)最少的服務(wù)器上
  • IP哈希 ip-hash — a hash-function is used to determine what server should be selected for the next request (based on the client’s IP address) 同一客戶端(IP)的連續(xù)的請求都會(huì)被分發(fā)到固定的一臺(tái)后端服務(wù)器上

前兩種方案(輪詢和最少連接)同一客戶端的請求會(huì)被隨機(jī)分配后不同的后端服務(wù)器上,如果后端是傳統(tǒng)的有狀態(tài)的WEB應(yīng)用,則需要在后端WEB容器上配置Session共享,非常麻煩。

后一種方案(IP哈希)可以識(shí)別請求端的IP,固定將其分配到某一臺(tái)后端服務(wù)器上,輕松解決了會(huì)話保持的問題。但I(xiàn)P哈希存在一個(gè)比較嚴(yán)重缺陷,即:客戶端必須能夠直連Nginx服務(wù)器,他們之間不能再插入其它層級(jí),否則Nginx就識(shí)別不到客戶端的IP了。

那除了IP HASH外,還有沒有其它方式能夠進(jìn)行會(huì)話保持呢?

Cookie Insertion

以下是官網(wǎng)的關(guān)于Cookie Insertion的介紹:

Cookie Insertion:NGINX Plus adds a session cookie to the first response from the upstream group to a given client, identifying which server generated the response (in an encoded fashion). Subsequent requests from the client include the cookie value and NGINX Plus uses it to route the request to the same upstream server:

Cookie植入:

  1. 客戶端第一次訪問時(shí),負(fù)載均衡服務(wù)在返回請求中植入cookie(在HTTP/HTTPS響應(yīng)報(bào)文中插入)
  2. 下次客戶端攜帶此cookie訪問,負(fù)載均衡服務(wù)會(huì)將請求定向轉(zhuǎn)發(fā)給之前記錄到的后端服務(wù)器上。

Cookie植入原理上就是劫持HTTP請求,偽造cookie,它可以在Nginx無法獲取客戶端IP時(shí),也依然能夠完成會(huì)話保持的功能,只要客戶端支持Cookie即可。

購買并使用Nginx Plus

要使用Cookie Insertion,最簡便的方式是使用商業(yè)版本的Nginx Plus,默認(rèn)支持。

以下是官網(wǎng)關(guān)于會(huì)話保持的介紹:
https://www.nginx.com/products/session-persistence/

Cookie Insertion的配置非常簡單:

upstream backend {
    server webserver1;
    server webserver2;
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

于是順道了解了一下Nginx Plus的來龍去脈:

  • 2002年,來自俄羅斯的Igor Sysoev使用C語言開發(fā)了NGINX;
  • 2004年,NGINX開放源碼,基于BSD開源許可。
  • 2006年 - 2016年,NIGNX Rlease版本歷經(jīng)0.5, 0.6, 0.7, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 1.9, 1.10, 1.11, 當(dāng)前最新版本為1.11.3;
  • 2011年,Sysoev成立了NGINX公司,NGINX PLUS是其第一款產(chǎn)品,也是NGINX的商業(yè)版本;
  • 2013年 - 2016年,自NGINX Plus Initial Release (R1)版本發(fā)布以來,NGINX商業(yè)版本已更新至NGINX Plus Release 9 (R9);

NGINX Plus is the all-in-one application delivery platform for the modern web.
All-In-One的NGINX PLUS對(duì)比開源版本重點(diǎn)增加了若干企業(yè)特性,包括更完善的七層、四層負(fù)載均衡,會(huì)話保持,健康檢查,實(shí)時(shí)監(jiān)控和管理等。

可看了一下最便宜的BASIC版本也要2500刀每年,淚奔,果斷PASS。
https://www.nginx.com/products/buy-nginx-plus/

使用第三方模塊nginx-sticky-module

流傳最廣的是一個(gè)名為 nginx-sticky-module 的第3方的模塊 ,不過已經(jīng)有多年沒有維護(hù)。

GoogleCode頁面(需扶墻): https://code.google.com/archive/p/nginx-sticky-module/downloads

下載 nginx-sticky-module-1.1.tar.gz ,看看能否配合較新的 nginx-1.12.1 使用。

Nginx下載地址: https://nginx.org/en/download.html

選擇Stable version中的nginx-1.12.1

簡單編譯一下試試:

./configure --prefix=/nginx/nginx-1.12.1 --add-module=/nginx/src/nginx-sticky-module-1.1  
make && make install

果然報(bào)錯(cuò):

/nginx/src/nginx-sticky-module-1.1/ngx_http_sticky_module.c: In function ‘ngx_http_get_sticky_peer’:
/nginx/src/nginx-sticky-module-1.1/ngx_http_sticky_module.c:333: error: assignment makes pointer from integer without a cast
make[1]: *** [objs/addon/nginx-sticky-module-1.1/ngx_http_sticky_module.o] Error 1
make[1]: Leaving directory `/nginx/src/nginx-1.12.1'
make: *** [build] Error 2

代碼太老舊,沒有細(xì)查的必要了,棄用。

使用第三方模塊nginx-sticky-module-ng

另外還有一個(gè)比較新的模塊 nginx-sticky-module-ng ,源碼托管在BitBucket上:
https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/

在Downloads/Tags下,找到1.2.6的源碼并下載,或者直接下載Master分支上最新的代碼。

在編譯之前,首先來動(dòng)手修復(fù)一個(gè)BUG。。。
找到ngx_http_sticky_misc.c,添加以下兩個(gè)include:

#include <openssl/sha.h>
#include <openssl/md5.h>

第三方的東西湊和著用吧。。。

編譯一下試試:

./configure --prefix=/nginx/nginx-1.12.1 --add-module=/nginx/src/nginx-sticky-module-ng
make && make install

編譯很順利,配置一下試試吧:
配置上非常簡單,只需要在upstream{}中加入sticky即可。

upstream {
  sticky;
}

Sticky的詳細(xì)配置說明:
sticky [name=route] [domain=.foo.bar] [path=/] [expires=1h]
[hash=index|md5|sha1] [no_fallback] [secure] [httponly];

  • name: the name of the cookies used to track the persistant upstream srv; default: route
  • domain: the domain in which the cookie will be valid default: nothing. Let the browser handle this.
  • path: the path in which the cookie will be valid default: /
  • expires: the validity duration of the cookie default: nothing. It's a session cookie. restriction: must be a duration greater than one second
  • hash: the hash mechanism to encode upstream server. It cant' be used with hmac. default: md5
  • md5|sha1: well known hash
  • index: it's not hashed, an in-memory index is used instead, it's quicker and the overhead is shorter Warning: the matching against upstream servers list is inconsistent. So, at reload, if upstreams servers has changed, index values are not guaranted to correspond to the same server as before! USE IT WITH CAUTION and only if you need to!
  • hmac: the HMAC hash mechanism to encode upstream server It's like the hash mechanism but it uses hmac_key to secure the hashing. It can't be used with hash. md5|sha1: well known hash default: none. see hash.
  • hmac_key: the key to use with hmac. It's mandatory when hmac is set default: nothing.
  • no_fallback: when this flag is set, nginx will return a 502 (Bad Gateway or Proxy Error) if a request comes with a cookie and the corresponding backend is unavailable.
  • secure enable secure cookies; transferred only via https
  • httponly enable cookies not to be leaked via js

下面給出一個(gè)完整的配置:

worker_processes  4;

events {
    worker_connections 1024;
}

http {
    upstream proxy_admin {
        #ip_hash;
        sticky;
        server 192.168.1.61:19022 weight=10 max_fails=3 fail_timeout=20s;
        server 192.168.1.62:19022 weight=10 max_fails=3 fail_timeout=20s;
    }

    server {
        listen       29022;
        server_name  proxy_admin;
        location / {
            root   html;
            proxy_pass   http://proxy_admin;
            proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
            proxy_redirect off;
            proxy_ignore_client_abort on;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_connect_timeout 60;
            proxy_send_timeout 60;
            proxy_read_timeout 60;
            proxy_buffer_size 4k;
            proxy_buffers 4 32k;
            proxy_busy_buffers_size 64k;
            proxy_temp_file_write_size 64k;
            client_body_buffer_size 128k;
        }
    }
}

測試,用瀏覽器訪問一下試試:
http://IP:29022/mobagent-admin/
如果在控制臺(tái)中的Cookie中看到一個(gè)名為route的鍵,就說明基本上成功了。
然后多請求幾次,看看后端服務(wù)的日志,最終確認(rèn)一下,是不是只指向了同一臺(tái)服務(wù)器。

注意事項(xiàng)

最后,請注意,因?yàn)閚ginx-sticky-module-ng的可靠性未經(jīng)長時(shí)間驗(yàn)證,請勿直接用于生產(chǎn)系統(tǒng)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容