十三週一次課(6月12日)

12.17 Nginx負載均衡

  • 代理一臺機器稱爲代理 ,代理兩臺機器就可以稱爲負載均衡
  • 代理服務器後面可以有多個web服務器,多個web服務器去提供服務的時候,就可以實現一個負載均衡的功能
  • 正常情況下,用戶訪問web服務器,是一臺一臺去請求;要麼就是指定一個IP,把這域名解析到多臺服務器上
  • 案例
  • 用戶1 –> web1服務器
  • 用戶2 –> web2服務器
  • 假設這時web1服務器掛掉了(宕機),用戶1因爲解析到了web1,但web1宕機了,所以就無法正常訪問
  • 這時候若是用nginx的負載均衡,在web1宕機後,代理服務器就不會把請求發送給web1,這就是代理的一個優點,負載均衡的優點
  • 配置負載均衡,負載均衡的配置藉助了upstream 模塊
    這裏將qq.com作爲演示對象
    dig命令查看解析的IP——>yum install -y bind-utils
# dig www.qq.com @202.96.134.133

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> www.qq.com @202.96.134.133
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46092
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 11

;; QUESTION SECTION:
;www.qq.com.                    IN      A

;; ANSWER SECTION:
www.qq.com.             3600    IN      A       182.254.34.74

;; AUTHORITY SECTION:
www.qq.com.             6722    IN      NS      ns-os1.qq.com.
www.qq.com.             6722    IN      NS      ns-cmn1.qq.com.
www.qq.com.             6722    IN      NS      ns-cnc1.qq.com.
www.qq.com.             6722    IN      NS      ns-tel1.qq.com.

;; ADDITIONAL SECTION:
ns-os1.qq.com.          473     IN      A       103.7.29.14
ns-os1.qq.com.          473     IN      A       184.105.206.121
ns-os1.qq.com.          473     IN      A       203.205.147.152
ns-cmn1.qq.com.         1906    IN      A       182.254.49.112
ns-cmn1.qq.com.         1906    IN      A       183.232.120.59
ns-cmn1.qq.com.         1906    IN      A       121.51.32.102
ns-cnc1.qq.com.         1826    IN      A       140.207.128.140
ns-cnc1.qq.com.         1826    IN      A       223.167.83.104
ns-cnc1.qq.com.         1826    IN      A       111.161.107.195
ns-tel1.qq.com.         298     IN      A       14.17.42.40
ns-tel1.qq.com.         298     IN      A       14.17.32.211

;; Query time: 10 msec
;; SERVER: 202.96.134.133#53(202.96.134.133)
;; WHEN: Sat Jun 09 11:58:39 CST 2018
;; MSG SIZE  rcvd: 307

會看到返回出兩個IP,這個就是域名解析,也就是qq.com解析到了兩個IP上
這時候就可以用這兩個IP:14.17.42.40和14.17.32.211,去 做負載均衡
vim一個vhost配置文件

vim /usr/local/nginx/conf/vhost/www.qq.com_upstream.conf       
#upstream來指定多個web server
upstream qq_com
{
    ip_hash;
    server 14.17.42.40:80;
    server 14.17.32.211:80;
}
server
{
    listen 80;
    server_name www.qq.com;
    location /
    {
        proxy_pass      http://qq_com;
        proxy_set_header Host   $host;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    access_log /www/wwwlogs/${host}_access.log format1;
}

upstream來指定多個web server
當有多個服務器同時對一個域名提供服務的時候,長時間訪問一個域名,在一定的時效內,會出現需要重新登錄或者是說跳轉到另外一個地址的服務器上;ip_hash,就是使通過這個代理訪問的同一個域名的多個IP的服務器是,始終保持在一個IP上對這個域名進行訪問
在未加載配置的時候,本機去訪問qq.com,回去訪問默認虛擬主機

檢查配置文件語法,並重新加載


[root@aaa abc.com]# curl -xlocalhost:80 -I www.qq.com
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 13 Mar 2018 19:18:51 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding

[root@aaa abc.com]# curl -xlocalhost:80 -I www.qq.com
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 13 Mar 2018 19:18:52 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding

nginx不支持去代理https,也就是在配置文件中的server 後不能寫443,是不支持的,只能代理http、tcp
若想要實現代理https,nginx監聽443端口,但web服務必須是80端口

12.18 ssl原理

瀏覽器發送一個https的請求給服務器;
服務器要有一套數字證書,可以自己製作(後面的操作就是自己製作的證書),也可以向組織申請,區別就是自己頒發的證書需要客戶端驗證通過,纔可以繼續訪問,而使用受信任的公司申請的證書則不會彈出>提示頁面,這套證書其實就是一對公鑰和私鑰;
服務器會把公鑰傳輸給客戶端;
客戶端(瀏覽器)收到公鑰後,會驗證其是否合法有效,無效會有警告提醒,有效則會生成一串隨機數,並用收到的公鑰加密;
客戶端把加密後的隨機字符串傳輸給服務器;
服務器收到加密隨機字符串後,先用私鑰解密(公鑰加密,私鑰解密),獲取到這一串隨機數後,再用這串隨機字符串加密傳輸的數據(該加密爲對稱加密,所謂對稱加密,就是將數據和私鑰也就是這個隨機字符串>通過某種算法混合在一起,這樣除非知道私鑰,否則無法獲取數據內容);
服務器把加密後的數據傳輸給客戶端;
客戶端收到數據後,再用自己的私鑰也就是那個隨機字符串解密;
十三週一次課(6月12日)

12.19 生成ssl密鑰對

cd /etc/nginx/vhost
openssl genrsa -des3 -out tmp.key 2048  //key文件爲私鑰
openssl rsa -in tmp.key -out aaa.com.key //轉換key,取消密碼 
rm -f tmp.key
openssl req -new -key aaa.com.key -out aaa.com.csr  //生成證書請求文件,需要拿這個文件和私鑰一起生產公鑰文件
openssl x509 -req -days 365 -in aaa.com.csr -signkey aaa.com.key -out aaa.com.crt
這裏的aaa.com.crt爲公鑰

1、創建私鑰並取消其密碼
準備工作:需要使用到openssl命令,故安裝其rpm包!

[root@aiker ~]# rpm -qf `which openssl`  //搜索openssl命令依賴的安裝包!
openssl-1.0.1e-60.el7_3.1.x86_64
[root@aiker ~]# yum install -y openssl  //安裝;
[root@aiker ~]# cd /etc/nginx/vhost/
[root@aiker vhost]# openssl genrsa -des3 -out tmp.key 2048  //生成長度2048的rsa私鑰.key 文件;
Enter pass phrase for tmp.key:
Verifying - Enter pass phrase for tmp.key: 
[root@aiker vhost]# openssl rsa -in tmp.key -out aaa.com.key  //取消私鑰的密碼,並輸出爲aaa.com.key
Enter pass phrase for tmp.key:
writing RSA key
[root@aiker vhost]# rm -f tmp.key   //刪除原來的私鑰;

2、生成證書請求文件--通過證書文件和私鑰生成公鑰!

[root@aiker vhost]# openssl req -new -key aaa.com.key -out aaa.com.csr  //生成.csr證書請求文件;
[root@aiker vhost]# openssl x509 -req -days 365 -in aaa.com.csr -signkey aaa.com.key -out aaa.com.crt  //生成.crt的公鑰
x.key -out aaa.com.crt
Signature ok
subject=/C=XX/L=Default City/O=Default Company Ltd
Getting Private key

12.20 Nginx配置ssl

 vim /etc/nginx/vhost/aaa.com.conf//加入如下內容
server
{
    listen 443;
    server_name aaa.com;
    index index.html index.php;
    root /data/wwwroot/aaa.com;
    ssl on;
    ssl_certificate aaa.com.crt;
    ssl_certificate_key aaa.com.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}
nginx -t && nginx -s reload //若報錯unknown directive “ssl” ,需要重新編譯nginx,加上--with-http_ssl_module
 mkdir /data/wwwroot/aaa.com
 echo “ssl test page.”>/data/wwwroot/aaa.com/index.html
 編輯hosts,增加127.0.0.1 aaa.com
 curl https://aaa.com/

1、編輯配置文件

[root@aiker test.com]# vim /etc/nginx/vhost/aaa.com.conf  //加入如下內容
server
{
    listen 443;                     //定義監聽端口443
    server_name aaa.com;          //定義域名
    index index.html index.php;
    root /data/wwwroot/aaa.com;   //網站根目錄
    ssl on;                         //開啓ssl
    ssl_certificate aaa.com.crt;       //公鑰
    ssl_certificate_key aaa.com.key;   //私鑰
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  //一般這三個協議 
}

2、重新加載

nginx -t && nginx -s reload //若報錯unknown directive “ssl” ,需要重新編譯nginx,加上--with-http_ssl_module

3、創建網站主目錄及頁面

[root@aiker ~]# mkdir /data/wwwroot/aaa.com

4、編輯hosts,訪問aaa.com
echo “ssl test page.”>/data/wwwroot/aaa.com/index.html
編輯hosts,增加127.0.0.1 aaa.com
[root@aiker ~]# curl https://aaa.com
curl: (60) Peer's certificate issuer has been marked as not trusted by the user.   //提示證書不信任;
More details here: http://curl.haxx.se/docs/sslcerts.html
……

試試網頁訪問:
注意:需要清除防火牆iptalbes -F

或者增加一行iptables規則:iptables -I INPUT -p tcp --dport 443 -j ACCEPT

擴展

針對請求的uri來代理

場景:1臺nginx去代理4臺apache
需求:根據不同的請求uri 代理到不同的apache
nginx的配置:

    upstream aa.com {         
                      server 192.168.0.121;
                      server 192.168.0.122;  
     }
    upstream bb.com {  
                       server 192.168.0.123;
                       server 192.168.0.124;
        }
    server {
        listen       80;
        server_name  www.abc.com;
        location ~ aa.php
        {
            proxy_pass http://aa.com/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
         location ~ bb.php
        {
              proxy_pass http://bb.com/;
              proxy_set_header Host   $host;
              proxy_set_header X-Real-IP      $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          }
}

根據訪問的目錄來區分後端的web

我的需求: 當請求的目錄是 /aaa/ 則把請求發送到機器a,當請求的目錄爲/bbb/則把請求發送到機器b,除了目錄/aaa/與目錄/bbb/外,其他的請求發送到機器b

我的配置文件內容爲:

upstream aaa.com
{
            server 192.168.111.6;
}
upstream bbb.com
{
            server 192.168.111.20;
}
server {
        listen 80;
        server_name li.com;
        location /aaa/
        {
            proxy_pass http://aaa.com/aaa/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /bbb/
        {
            proxy_pass http://bbb.com/bbb/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /
        {
            proxy_pass http://bbb.com/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

說明:
1 以上配置文件中的 aaa.com 以及 bbb.com 都是自定義的,隨便寫。
2 upstream 中的server 可以寫多個,例如

upstream aaa.com 
{
            server 192.168.111.6;
            server  192.168.111.4;
            server  192.168.111.5;
}

3 proxy_pass http://aaa.com/aaa/ 這裏必須要加這個目錄,不然就訪問到根目錄了。
4 實際上,上述配置文件中, localtion /bbb/ 部分是可以省略掉的,因爲後邊的 location / 已經包含了/bbb/,所以即使我們不去定義 localtion /bbb/ 也是會訪問到 bbb.com 的。

nginx長連接

網站使用程序 discuzx3 訪問都正常,只有用戶登陸存在異常,具體的情況是這樣的:
用戶登陸後,會馬上顯示未登陸,然後刷新一下或者多下又變成了登陸中。

這個問題很顯然是由於session導致,後臺有多個web機器,當用戶登陸後,會把登陸態session保存到當前web,但是再次發送請求時則會到另一臺機器,所以原來的session信息找不到了。解決這個問題有兩個思路:

  1. 可以把session時時同步到另外的機器。
  2. 可以讓前端的調度器保持長連接,也就是說某個用戶的請求在某一時間段內始終抓發到固定的一臺機器上。
    這兩種方式,第二種更容易實現。

我使用的是nginx的代理,其中nginx有一種算法支持長連接,具體配置是這樣的:

 upstream test {
            ip_hash;
server 192.168.109.5;
server 192.168.109.3;
}

關鍵代碼: ip_hash
這樣,nginx會把用戶的請求一直轉發到後端的某臺機器。

nginx算法分析

隨着互聯網信息的爆炸性增長,負載均衡(load balance)已經不再是一個很陌生的話題,顧名思義,負載均衡即是將負載分攤到不同的服務單元,既保證服務的可用性,又保證響應足夠快,給用戶很好的體驗。快速增長的訪問量和數據流量催生了各式各樣的負載均衡產品,很多專業的負載均衡硬件提供了很好的功能,但卻價格不菲(如F5 BIG-IP、Citrix NetScaler、Radware等等,雖然可以解決問題,但其高昂的價格卻往往令人望而卻步),這使得負載均衡軟件大受歡迎,nginx就是其中的一個。

nginx第一個公開版本發佈於2004年,2011年發佈了1.0版本。它的特點是穩定性高、功能強大、資源消耗低,從其目前的市場佔有而言,nginx大有與apache搶市場的勢頭。其中不得不提到的一個特性就是其負載均衡功能,這也成了很多公司選擇它的主要原因。本文將從源碼的角度介紹nginx的內置負載均衡策略和擴展負載均衡策略,以實際的工業生產爲案例,對比各負載均衡策略,爲nginx使用者提供參考。

nginx的負載均衡策略可以劃分爲兩大類:內置策略和擴展策略。內置策略包含加權輪詢和ip hash,在默認情況下這兩種策略會編譯進nginx內核,只需在nginx配置中指明參數即可。擴展策略有很多,如fair、通用hash、consistent hash等,默認不編譯進nginx內核。由於在nginx版本升級中負載均衡的代碼沒有本質性的變化,因此下面將以nginx1.0.15穩定版爲例,從源碼角度分析各個策略。

  1. 加權輪詢(weighted round robin)

輪詢的原理很簡單,首先我們介紹一下輪詢的基本流程。如下是處理一次請求的流程圖:

十三週一次課(6月12日)

圖中有兩點需要注意,第一,如果可以把加權輪詢算法分爲先深搜索和先廣搜索,那麼nginx採用的是先深搜索算法,即將首先將請求都分給高權重的機器,直到該機器的權值降到了比其他機器低,纔開始將請求分給下一個高權重的機器;第二,當所有後端機器都down掉時,nginx會立即將所有機器的標誌位清成初始狀態,以避免造成所有的機器都處在timeout的狀態,從而導致整個前端被夯住。

接下來看下源碼。nginx源碼的目錄結構很清晰,加權輪詢所在路徑爲nginx-1.0.15/src/http/ngx_http_upstream_round_robin.[c|h],在源碼的基礎上,針對重要的、不易理解的地方我加了註釋。首先看下ngx_http_upstream_round_robin.h中的重要聲明:
十三週一次課(6月12日)
image從變量命名中,我們就可以大致猜出其作用。其中,current_weight和weight的區別主要是前者爲權重排序的值,隨着處理請求會動態的變化,後者是配置值,用於恢復初始狀態。

接下來看下輪詢的創建過程,代碼如下圖所示。
十三週一次課(6月12日)
image這裏有個tried變量需要做些說明。tried中記錄了服務器當前是否被嘗試連接過。他是一個位圖。如果服務器數量小於32,則只需在一個int中即可記錄下所有服務器狀態。如果服務器數量大於32,則需在內存池中申請內存來存儲。對該位圖數組的使用可參考如下代碼:
十三週一次課(6月12日)
image最後是實際的策略代碼,邏輯很簡單,代碼實現也只有30行,直接上代碼。

十三週一次課(6月12日)

  1. ip hash

ip hash是nginx內置的另一個負載均衡的策略,流程和輪詢很類似,只是其中的算法和具體的策略有些變化,如下圖所示:
十三週一次課(6月12日)
imageip hash算法的核心實現如下圖:
十三週一次課(6月12日)
image從代碼中可以看出,hash值既與ip有關又與後端機器的數量有關。經過測試,上述算法可以連續產生1045個互異的value,這是該算法的硬限制。對此nginx使用了保護機制,當經過20次hash仍然找不到可用的機器時,算法退化成輪詢。因此,從本質上說,ip hash算法是一種變相的輪詢算法,如果兩個ip的初始hash值恰好相同,那麼來自這兩個ip的請求將永遠落在同一臺服務器上,這爲均衡性埋下了很深的隱患。

  1. fair

fair策略是擴展策略,默認不被編譯進nginx內核。其原理是根據後端服務器的響應時間判斷負載情況,從中選出負載最輕的機器進行分流。這種策略具有很強的自適應性,但是實際的網絡環境往往不是那麼簡單,因此要慎用。

  1. 通用hash、一致性hash

這兩種也是擴展策略,在具體的實現上有些差別,通用hash比較簡單,可以以nginx內置的變量爲key進行hash,一致性hash採用了nginx內置的一致性hash環,可以支持memcache。

對上面的集中負載均衡算法進行測試(測試工具polygraph),考察下面三個關鍵的測試指標:

均衡性:是否能夠將請求均勻的發送給後端

一致性:同一個key的請求,是否能落到同一臺機器

容災性:當部分後端機器掛掉時,是否能夠正常工作

通過實際的對比測試,我們對nginx各個負載均衡策略進行了驗證。下面從均衡性、一致性、容災性以及適用場景等角度對比各種策略。
十三週一次課(6月12日)
image無論哪種策略都不是萬金油,在具體的場景下應該選擇哪種策略一定程度上依賴於使用者對這些策略的熟悉程度。希望本文的分析和測試數據能夠對讀者有所幫助,更希望有越來越多、越來越好的負載均衡策略產出。

參考資料

http://wiki.nginx.org/HttpUpstreamConsistentHash

http://wiki.nginx.org/HttpUpstreamFairModule

http://wiki.nginx.org/HttpUpstreamRequestHashModule

http://www.web-polygraph.org/

http://nginx.org/

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