Nginx負載均衡與反向代理——擴展功能(NGINX Plus)

本文主要是介紹了NGINX Plus的相關功能,橫跨了NGINX Plus R5/R6/R7/R9等各個不一樣版本的更新。涉及的是Nginx反向代理和負載均衡的更爲高級的用法。主要包含:HTTP負載均衡,HTTP長鏈接,TCP和UDP的負載均衡,上游鏈接數限制,最短期的均衡算法,Session一致性,實時健康檢查,DNS重解析,訪問控制,客戶端鏈接數限制,客戶端帶寬限制,無緩衝上傳文件,SSL/TLS優化,,緩存優化,API功能,配置的最佳實踐等。php

什麼是NGINX Plus?

顧名思義,就是Nginx的增強版或者擴展版。咱們知道Nginx是開源的、免費的,可是NGINX Plus的不少功能就須要收費了。Nginx Plus 能夠做爲一個負載均衡器,一個web服務器,還能夠做爲一個內容緩存。既然是Nginx的增強版,那無疑功能會比Nginx更增強大。NGINX Plus在開源Nginx已有的功能基礎上,提供了許多適合生產環境的專有功能,包括session一致性、實時更新API配置、有效的健康檢查等。html

NGINX Plus的版本更新

NGINX Plus R5 和更新的版本能夠支持基於TCP應用的負載均衡(好比MySQL)。這就不只僅限制於Http的負載均衡,而是大大擴充了Nginx做爲負載均衡器的做用範圍。R6中TCP負載均衡功能獲得很大的擴充,加入了健康檢查、動態更新配置、SSL終端等。等到了R7,TCP負載均衡功能就基本和Http負載均衡差很少了。z再到了R9,就能夠支持UDP了。經過這些更新,NGINX Plus 遠遠超過了web應用的層面,成爲了一款意義更爲普遍的負載均衡器。畢竟協議是基礎層面的東西,支持的協議越多,應用面也越廣。從最初的Http/SMTP到TCP再到UDP,NGINX Plus一步步的變得愈來愈強大。mysql

開源Nginx和NGINX Plus 都支持HTTP, TCP, 和UDP應用的負載均衡。但NGINX Plus 提供了一些企業級別的功能,這些功能是收費的,包括session一致性,健康檢查,動態更新配置等。nginx

HTTP負載均衡

NGINX Plus對Http負載均衡作了不少功能優化,諸如HTTP 升級、長鏈接優化、內容壓縮和響應緩存等。在NGINX Plus中Http負載均衡的實現也很是簡單:web

http {
    upstream my_upstream {
        server server1.example.com;
        server server2.example.com;
    }

    server {
        listen 80;
        location / {
            proxy_set_header Host $host;
            proxy_pass http://my_upstream;
        }
    }
}

能夠經過proxy_set_header 指令來設置Host,而proxy_pass將請求轉發到上游的my_upstream中。redis

HTTP長鏈接

HTTP長鏈接——HTTP Keepalives,是指NGINX PLus和上游服務器創建的長鏈接。客戶端和NGINX PLus創建長鏈接的話,能夠在客戶端指定Http協議爲1.1/2.0。算法

HTTP協議是用的底層TCP協議來傳輸請求,接收響應的。HTTP1.1/2.0支持TCP的長鏈接或者重利用,以避免反覆的建立和銷燬TCP鏈接所帶來的開銷。sql

咱們看看客戶端到NGINX PLus之間的Http長鏈接:json

Http的長鏈接

NGINX是一個徹底意義的反向代理,在長鏈接上也絕不含糊。它管理因此來從客戶端到Nginx的長鏈接,一樣也會管理從Nginx到上游服務器的長鏈接,兩者是徹底獨立的後端

Nginx管理的長鏈接:

這裏寫圖片描述

NGINX 將鏈接上游服務器的空閒鏈接作了「緩存」,並不直接關掉它們。若是有請求過來,NGINX先從緩存的活躍鏈接中去拿一個使用,而不是立馬建立一個新的,若是緩存爲空那麼NGINX 再去新建一個鏈接。這種操做保持了和上游之間鏈接的最小必要的數目,從而下降了Nginx和上游服務器之間的延遲並減小了臨時端口的利用率,因此NGINX能處理大的併發。這種技術加上別的負載均衡技術,有時候能夠被稱爲鏈接池,或者鏈接複用。

爲了配置閒置長鏈接緩存,你須要指定幾個指令:proxy_http_version,proxy_set_header,keepalive

server {
    listen 80;
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1; # 只有Http1.1/2.0才能支持長鏈接
        proxy_set_header Connection "";
    }
}

upstream backend {
    server webserver1;
    server webserver2;

    # maintain a maximum of 20 idle connections to each upstream server
    keepalive 20; # 閒置長鏈接緩存時間爲20
}

TCP和UDP的負載均衡

做爲對HTTP協議的擴展,NGINX Plus能夠直接支持基於TCP和UDP協議的應用。基於TCP的如MySQL,支持UDP的如DNS 和RADIUS應用。對於TCP請求來講,NGINX Plus接收了客戶端的TCP請求,而後再建立一個TCP請求對上游服務器發起訪問。

stream {
    upstream my_upstream {
        server server1.example.com:1234;
        server server2.example.com:2345;
    }

    server {
        listen 1123 [udp];
        proxy_pass my_upstream; #注意這裏沒有http://了
    }
}

對TCP請求的支持出如今NGINX Plus R5,R6和R7版本主要是在優化這個功能,到R7時TCP請求的負載均衡已經強大到足夠媲美Http負載均衡了,到了R9,則能夠支持UDP了。這裏先有個印象,後面會更加詳細介紹TCP負載均衡功能。

上游鏈接數限制

你還能夠爲負載均衡作鏈接數量限制。這裏說的鏈接是指NGINX Plus發給上游服務器的HTTP/TCP/UDP請求鏈接(對於UDP則是會話)。有了鏈接數限制的功能,當上遊服務器的Http/TCP鏈接數量,或者UDP的會話數量超過必定的值時,NGINX Plus就再也不建立新的鏈接或者會話。客戶端多出的請求鏈接能夠被放進隊列等候,也能夠不被處理。能夠經過max_conns,queue指令來實現這一點:

upstream backend {
    zone backends 64k;
    queue 750 timeout=30s;

    server webserver1 max_conns=250;
    server webserver2 max_conns=150;
}

server指令表示webserver1 最多承載250個鏈接而webserver2 最多150個,多出來的能夠放在隊列queue當中等候。在隊列queue中等候的鏈接數量和等候時間也是有限制的。當webserver1 和webserver2 鏈接數下降到各自最大鏈接數如下時,等候在隊列queue中的鏈接隨時就補上去。
queue 750 timeout=30s表示總共能夠有750個鏈接排隊等候,每一個鏈接等候30s。

Limiting connections 是十分有用的,能夠爲客戶端提供可持續可預見的服務——沒必要由於某臺server負載過大致使掛掉。通常來講一臺server大概能承載多少負荷是能夠經過某些手段測試出來的,所以把這個可承受的上線做爲max_conns指令的值即可以保證server的相對安全。

最少時間的均衡算法

在NGINX Plus R6中增長了一種新的均衡算法——Least Time,將相應時間也考慮進去,算得上對最少鏈接均衡算法(Least Connections)的擴展。

這種算法同時考慮當前鏈接數和鏈接池裏各個節點的平均響應時間。目的是使得當前請求選擇當下響應更快、鏈接更少的服務器,而不是選擇響應更慢、鏈接更多的。

當鏈接池的各個服務器節點有着明顯不一樣的響應延時時,這種算法就要優於其餘的幾種(round-robin/ip-hash/lease connections)。一個典型的應用場景是,若是有兩個分佈在不一樣的地域的數據中心,那麼本地的數據中心就要比異地的數據中心延時要少得多,這個時候就不能僅僅考慮當下鏈接數了,這個響應的延時也要被計入考量。Least Time算法就更傾向於選擇本地的,固然這只是「更傾向於」的問題,並不能代替Nginx最基本的錯誤轉移功能,哪怕本地的數據中心響應再快,若是它掛掉了Nginx Plus也能立刻切換到遠端數據中心。

這裏寫圖片描述

「最少時間」能夠有兩種計算方式,一種是從請求發出到上流服務器接返回響應頭部算的時間,另外一種是從請求發出到接收到所有請求體的時間,分別以header_timeresponse_time來表示。

Session一致性

Session一致性(Session Persistence)問題除了能夠經過指定ip-hash的均衡算法來實現,還有更爲通用的實現方式,這是在NGINX Plus 中實現的。

NGINX Plus能夠識別用戶Session,從而可以鑑別不一樣的客戶端,而且能夠未來自同一個客戶端的請求發往同一個上游服務器。這在當應用保存了用戶狀態的狀況下很是有用,能夠避免負載均衡器按照某個算法將請求發到別的服務器上去。另外,在共享用戶信息的集羣服務器這種方式也很是有用。

session一致性的要求同一個客戶端每次的請求都選擇同一個服務器,而負載均衡要求咱們利用一種算法去服務器鏈接池裏面去選擇下一個,那麼這兩種矛盾的方式能夠共存麼?能夠的,NGINX Plus按照以下的步驟決策到底選用哪種:

  • 若是request匹配某個Session一致性的規則,那麼根據這個規則選取上游服務器;
  • 若是沒有匹配上或者匹配的服務器沒法使用,那麼使用負載均衡算法選擇上游服務器;

爲了能保證session一致性,Nginx Plus提供了sticky cookie,sticky learn和sticky route幾種規則。

對於 sticky cookie 規則,當客戶端的第一個請求選擇了某個上游服務器,並從這個上游服務器返回響應時,NGINX Plus 爲這個響應添加一個session cookie,用來鑑別這個上游服務器。當後面的請求再過來時,NGINX Plus取出這個cookie,分析是哪一臺服務器,再把請求發往這臺相同的服務器。

使用指令sticky cookie,配置以下:

upstream backend {
    server webserver1;
    server webserver2;

    sticky cookie srv_id expires=1h domain=.example.com path=/; 
}

cookie的名字就叫srv_id,用來「記住」是哪個server;過時時間1h,domain爲.example.com;path爲/
NGINX Plus在第一次響應中,插入一個名稱爲srv_idcookie,用來「記住」這第一次請求是發個哪一個上游的,後面的請求帶上這個cookie,一樣再被NGINX Plus甄別一下,再發往同一個的服務器。這樣就能保證session的一致了。

sticky route 規則

sticky cookie規則相似,只不過「記住」上游服務器的方式不一樣而已。
在客戶端發起第一次請求時,接收它的服務器爲其分配一個route,此後這個客戶端發起的全部請求都要帶上這個route信息,或者在cookie中或者在uri中。而後和server指令中的route參數作對比,決定選取哪一個server。若是指定的服務器沒法處理,那交給負載均衡算法去選擇下一個服務器。

map $cookie_jsessionid $route_cookie {
    ~.+\.(?P<route>\w+)$ $route;
}

map $request_uri $route_uri {
    ~jsessionid=.+\.(?P<route>\w+)$ $route;
}

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;
    # select first non-empty variable; it should contain either 'a' or 'b'
    sticky route $route_cookie $route_uri;
}

在這裏,route在JSESSIONIDcookie中選擇,如其包含a那麼選擇服務器backend1;如其包含b則選擇backend2,若是都不包含那麼在$request_uri 中再作相似的選擇,以此類推。

無論是選哪一種方式保持session一致,若是選擇出的server沒法使用,那麼將會按照負載均衡算法(如round-robin)在服務器列表中的選擇下一臺server繼續處理。

實時健康檢查

前面提到過,Nginx有兩大功能:一個是擴展,增長更多的server以知足更大的併發;二是檢測失效server,以便及時排除。那麼,如何定義一個「失效server」(failed server)就變得很是重要。這一節就是來討論這個問題——實時健康檢查(Active Health Checks)。這是NGINX Plus 纔有的功能,而且是收費的。

開源版本NGINX 能夠提供簡單的健康檢查,而且能夠自動作故障轉移。可是如何定義一個上游server「失效」開源NGINX 卻作的很簡單。NGINX Plus爲此提供了一個能夠自定義的、綜合式的評判標準,除此以外NGINX Plus還能夠平緩的添加新的服務器節點到集羣當中。這個功能使得NGINX Plus可能甄別更爲多元化的服務器錯誤,十分有效的增長了HTTP/TCP/UDP應用的可靠性。
這裏要用到的指令有:health_check,match 等指令:

upstream my_upstream {
    zone my_upstream 64k;
    server server1.example.com slow_start=30s;
}

server {
    # ...
    location /health {
        internal;
        health_check interval=5s uri=/test.php match=statusok;
        proxy_set_header HOST www.example.com;
        proxy_pass http://my_upstream
    }
}

match statusok {
    # 在/test.php 作健康檢查
    status 200;
    header Content-Type = text/html;
    body ~ "Server[0-9]+ is alive";
}

health_checkinterval=5s表示每隔5s檢測一次;uri=/test.php表示在/test.php裏進行健康檢查,NGINX Plus自動發起uri的請求,uri能夠自定義,你在裏面具體執行檢查的邏輯,好比mysql/redis這些是否正常,而後做出必定的響應;而後在match指令中,就經過一些規則來匹配/test.php的響應。/test.php的響應能夠包括status,header,body這些,供後面這些指令作匹配。所有檢查經過,就算健康,server被標記爲活躍;若是一項匹配未經過,好比Content-Type = text/json或者status = 201那都算檢測失敗,server不健康,被標記爲不活躍。

DNS重解析

Nginx Plus一啓動就會進行DNS解析而且自動永久緩存解析出的域名和IP,可是某些情形下須要從新解析下,這時候可使用下面的指令來實現:

resolver 127.0.0.11 valid=10s;

upstream service1 {
    zone service1 64k;
    server www.example.com  service=http resolve;
}

127.0.0.11是默認的DNS服務器的地址,此例中NGINX Plus每10s中DNS服務器發起一次從新解析的請求。

訪問控制

NGINX Plus Release 7主要給增長了TCP負載均衡的安全性。好比訪問控制(Access Controls)和DDoS保護。
你如今能夠容許或者拒絕對作反向代理的或者作負載均衡的TCP服務器的訪問,僅僅經過配置簡單的IP或者一個IP範文就能實現:

server {
    # ...
    proxy_set_header Host www.example.cn;
    proxy_pass http://test;
    deny 72.46.166.10;
    deny 73.46.156.0/24;
    allow all;
}

第一個deny指令拒絕一個IP的訪問,第二個拒絕一個IP範圍,除去這兩個剩下的都是被容許訪問的。被拒絕訪問的IP,會被返回403錯誤。

客戶端鏈接數限制

除了能夠限定上游服務器鏈接數,還能夠限定客戶端鏈接數,NGINX Plus R7 中實現了這個功能。你能夠限制客戶端發往由NGINX Plus代理的TCP應用的請求數量,防止對TCP的請求數量過多。在你的應用中,可能一部分的比另外一部分要慢一些。好比說,請求你的應用的某塊,將會產生大量的MySQL請求,或者fork出一大堆的work進程。那麼攻擊者將會利用這點產生成千上萬個請求,導致你的服務器負載太重而癱瘓。

可是有了鏈接數限制功能,你能夠經過配置limit_conn my_limit_conn指令限制同一個客戶端(IP)所能發起的最大請求數,以此將上述的攻擊風險降到最低。

stream {
    limit_conn_zone $binary_remote_addr zone=my_limit_conn:10m;
    # ...
    server {
        limit_conn my_limit_conn 1;
        # ...
    }
}

這條指令限定了每一個IP同時只能有一個鏈接。

客戶端帶寬限制

R7 還新增了一項功能——限制每一個客戶端鏈接的上傳和下載的最大帶寬(Bandwidth Limiting) 。

server {
    # ...
    proxy_download_rate 100k;
    proxy_upload_rate  50k;
}

有了這個配置,客戶端最多隻能以100kbytes/s的速度下載,以50kbytes/s的速度上傳。由於客戶端能夠開多個鏈接,所以若是要限制總的上傳/下載速度,同時還得限制下單個客戶端的鏈接數。

無緩衝上傳文件

這是在R6中增長的功能。你能夠在R6和之後的版本中使用無緩衝的上傳,意味Nginx Plus能夠經過更大的Http請求好比上傳。無緩衝的上傳能夠在這些請求一過來便進行上傳,而不是像以前那樣先是緩衝全部的上傳內容,再將其轉發給你上游服務器。

默認狀況下,Nginx 在上傳時,接收到數據時會先放進緩衝區進行緩衝,以免將資源和基於worker進程的後端腳本綁定,可是針對事件驅動的後端語言如Node.js,緩衝是幾乎沒有必要的。這個修改改進了服務器對上傳大文件的響應性,由於應用能夠一接收到數據就立刻對作出響應,使得上傳進度條變成實時的和準確的。一樣,這個改進也減小了磁盤I/O。

SSL/TLS優化

在R6中,能夠在和上游的HTTPS 或者 uwSGI 服務器打交道時爲客戶端提供一個證書。這大大提升了安全性,尤爲是在和不受保護網絡上的安全服務進行通訊的時候。R6 支持IMAP, POP3, 和SMTP的SSL/TLS 客戶端認證。

緩存優化

proxy_cache 指令能夠支持變量了,這個簡單的改進覺得着你能夠定義幾個基於磁盤的緩存,而且根據請求數據作自由的選擇。當你打算建立巨大的內容緩存,而且將其保存到不一樣的磁盤時是很是有用的。

API功能

upstreem模塊的一些指令,不光能夠經過手動去修改,還能夠經過Restful Api的方式去修改,而且立刻自動更新。有了這個功能,NGINX Plus的一些功能,你均可以經過API的方式去改變。應用性獲得很大提高。固然這也是收費的:

upstream backend {
    zone backends 64k;
    server 10.10.10.2:220 max_conns=250;
    server 10.10.10.4:220 max_conns=150;
}

server {
    listen 80;
    server_name www.example.org;

    location /api {
        api write=on;
    }
}

有了API,你就可使用curl工具來動態修改配置了,好比用POST命令來增長一個集羣的節點:

$ curl -iX POST -d '{"server":"192.168.78.66:80","weight":"200","max_conns":"150"}' http://localhost:80/api/1/http/upstreams/backend/servers/

至關於添加了一個這樣的配置:

upstream backend {
    zone backends 64k;
    server 10.10.10.2:220 max_conns=250;
    server 10.10.10.4:220 max_conns=150;
    #此處是經過api添加的
    server 192.168.78.66:80 weight=200 max_conns=150;
}

若是須要修改一個節點配置,你能夠用服務器節點在鏈接池中的天然順序(從0開始)做爲它們各自惟一的ID,而後使用PATCH/DELETE方法去操做它們:

$ curl -iX PATCH -d '{"server":"192.168.78.55:80","weight":"500","max_conns":"350"}' http://localhost:80/api/1/http/upstreams/backend/servers/2

這條命令是修改以上鍊接池中的第三個server 192.168.78.66:80 max_conns=200;爲:

server 192.168.78.55:80 weight=500  max_conns=350;

若是要返回全部的節點信息,可使用:

$ curl -s http://localhost:80/api/1/http/upstreams/backend/servers/

返回的是一個JSON字符串。

{
      "backup": false,
      "down": false,
      "fail_timeout": "10s",
      "id": 0,
      "max_conns": 250,
      "max_fails": 1,
      "route": "",
      "server": "10.10.10.2:220",
      "slow_start": "0s",
      "weight": 1
      },
      {
      "backup": false,
      "down": false,
      "fail_timeout": "10s",
      "id": 1,
      "max_conns": 150,
      "max_fails": 1,
      "route": "",
      "server": "10.10.10.4:220",
      "slow_start": "0s",
      "weight": 1
      },
      {
      "backup": false,
      "down": false,
      "fail_timeout": "10s",
      "id": 2,
      "max_conns": 200,
      "max_fails": 1,
      "route": "",
      "server": "192.168.78.66:80",
      "slow_start": "0s",
      "weight": 200
      }
  }

配置的最佳實踐

爲不一樣個應用配置建立各自的目錄和文件,並用include指令再合併到一塊兒是個很是好的習慣。標準的 NGINX Plus配置是將各個應用的配置文件放到各自的conf.d directory目錄下:

http {
    include /etc/nginx/conf.d/*.conf;
}
stream {
    include /etc/nginx/stream.d/*.conf;
}

http 和 stream 模塊的各自分屬不一樣的目錄,而在http 下的都是http請求的配置,stream 都是TCP/UDP請求的配置。沒有統一的標準,主要是看開發者本身能便於識別和修改。

相關文章
相關標籤/搜索