1. 程式人生 > >Nginx(四)------nginx 負載均衡

Nginx(四)------nginx 負載均衡

  在上一篇部落格我們介紹了 Nginx 一個很重要的功能——代理,包括正向代理和反向代理。這兩個代理的核心區別是:正向代理代理的是客戶端,而反向代理代理的是伺服器。其中我們又重點介紹了反向代理,以及如何通過 Nginx 來實現反向代理。那麼瞭解了Nginx的反向代理之後,我們要通過Nginx的反向代理實現另一個重要功能——負載均衡。

1、負載均衡的由來

  早期的系統架構,基本上都是如下形式的:

  

  客戶端傳送多個請求到伺服器,伺服器處理請求,有一些可能要與資料庫進行互動,伺服器處理完畢後,再將結果返回給客戶端。

  這種架構模式對於早期的系統相對單一,併發請求相對較少的情況下是比較適合的,成本也低。但是隨著資訊數量的不斷增長,訪問量和資料量的飛速增長,以及系統業務的複雜度增加,這種架構會造成伺服器相應客戶端的請求日益緩慢,併發量特別大的時候,還容易造成伺服器直接崩潰。很明顯這是由於伺服器效能的瓶頸造成的問題,那麼如何解決這種情況呢?

  我們首先想到的可能是升級伺服器的配置,比如提高CPU執行頻率,加大記憶體等提高機器的物理效能來解決此問題,但是我們知道摩爾定律的日益失效,硬體的效能提升已經不能滿足日益提升的需求了。最明顯的一個例子,天貓雙十一當天,某個熱銷商品的瞬時訪問量是極其龐大的,那麼類似上面的系統架構,將機器都增加到現有的頂級物理配置,都是不能夠滿足需求的。那麼怎麼辦呢?

  上面的分析我們去掉了增加伺服器物理配置來解決問題的辦法,也就是說縱向解決問題的辦法行不通了,那麼橫向增加伺服器的數量呢?這時候叢集的概念產生了,單個伺服器解決不了,我們增加伺服器的數量,然後將請求分發到各個伺服器上,將原先請求集中到單個伺服器上的情況改為將請求分發到多個伺服器上,將負載分發到不同的伺服器,也就是我們所說的負載均衡

  

  負載均衡完美的解決了單個伺服器硬體效能瓶頸的問題,但是隨著而來的如何實現負載均衡呢?客戶端怎麼知道要將請求傳送到那個伺服器去處理呢?

2、Nginx實現負載均衡

  Nginx 伺服器是介於客戶端和伺服器之間的中介,通過上一篇部落格講解的反向代理的功能,客戶端傳送的請求先經過 Nginx ,然後通過 Nginx 將請求根據相應的規則分發到相應的伺服器。

  

  主要配置指令為上一講的 pass_proxy 指令以及 upstream 指令。負載均衡主要通過專門的硬體裝置或者軟體演算法實現。通過硬體裝置實現的負載均衡效果好、效率高、效能穩定,但是成本較高。而通過軟體實現的負載均衡主要依賴於均衡演算法的選擇和程式的健壯性。均衡演算法又主要分為兩大類:

  靜態負載均衡演算法:主要包括輪詢演算法、基於比率的加權輪詢演算法或者基於優先順序的加權輪詢演算法。

  動態負載均衡演算法:主要包括基於任務量的最少連線優化演算法、基於效能的最快響應優先演算法、預測演算法及動態效能分配演算法等。

  靜態負載均衡演算法在一般網路環境下也能表現的比較好,動態負載均衡演算法更加適用於複雜的網路環境。

  例子:

①、普通輪詢演算法

  這是Nginx 預設的輪詢演算法。

例子:兩臺相同的Tomcat伺服器,通過 localhost:8080 訪問Tomcat1,通過 localhost:8081訪問Tomcat2,現在我們要輸入 localhost 這個地址,可以在這兩個Tomcat伺服器之間進行交替訪問。

  一、分別修改兩個Tomcat伺服器的埠為8080和8081。然後再修改Tomcat的首頁,使得訪問這兩個頁面時能夠區分。如下:

  修改埠號檔案為 server.xml :

  

  修改首頁的路徑為:webapps/ROOT/index.jsp

  

  修改完成之後,分別啟動這兩個Tomcat伺服器,然後分別輸入相應的地址埠號:

  輸入地址:localhost:8081

  

   輸入地址:localhost:8080

  

   二、修改 nginx 的配置檔案 nginx.conf 

 1     upstream OrdinaryPolling {
 2     server 127.0.0.1:8080;
 3     server 127.0.0.1:8081;
 4     }
 5     server {
 6         listen       80;
 7         server_name  localhost;
 8 
 9         location / {
10             proxy_pass http://OrdinaryPolling;
11             index  index.html index.htm index.jsp;
12         
13         }
14     }

  三、啟動 nginx。然後在瀏覽器輸入localhost 地址,觀看頁面變化:

   

②、基於比例加權輪詢

  上述兩臺Tomcat伺服器基本上是交替進行訪問的。但是這裡我們有個需求:

  由於Tomcat1伺服器的配置更高點,我們希望該伺服器接受更多的請求,而 Tomcat2 伺服器配置低,希望其處理相對較少的請求。

  那麼這時候就用到了加權輪詢機制了。

  nginx.conf 配置檔案如下:

 1     upstream OrdinaryPolling {
 2     server 127.0.0.1:8080 weight=5;
 3     server 127.0.0.1:8081 weight=2;
 4     }
 5     server {
 6         listen       80;
 7         server_name  localhost;
 8 
 9         location / {
10             proxy_pass http://OrdinaryPolling;
11             index  index.html index.htm index.jsp;
12         
13         }
14     }

  其實對比上面不加權的輪詢方式,這裡在 upstream 指令中多了一個 weight 指令。該指令用於配置前面請求處理的權重,預設值為 1。

  也就是說:第一種不加權的普通輪詢,其實其加權值 weight 都為 1。

  下面我們看頁面相應結果:

  

   明顯 8080 埠號出現的次數更多,試驗的次數越多越接近我們配置的比例。

③、基於IP路由負載

  我們知道一個請求在經過一個伺服器處理時,伺服器會儲存相關的會話資訊,比如session,但是該請求如果第一個伺服器沒處理完,通過nginx輪詢到第二個伺服器上,那麼這個伺服器是沒有會話資訊的。

  最典型的一個例子:使用者第一次進入一個系統是需要進行登入身份驗證的,首先將請求跳轉到Tomcat1伺服器進行處理,登入資訊是儲存在Tomcat1 上的,這時候需要進行別的操作,那麼可能會將請求輪詢到第二個Tomcat2上,那麼由於Tomcat2 沒有儲存會話資訊,會以為該使用者沒有登入,然後繼續登入一次,如果有多個伺服器,每次第一次訪問都要進行登入,這顯然是很影響使用者體驗的。

  這裡產生的一個問題也就是叢集環境下的 session 共享,如何解決這個問題?

  通常由兩種方法:

  1、第一種方法是選擇一箇中間件,將登入資訊儲存在一箇中間件上,這個中介軟體可以為 Redis 這樣的資料庫。那麼第一次登入,我們將session 資訊儲存在 Redis 中,跳轉到第二個伺服器時,我們可以先去Redis上查詢是否有登入資訊,如果有,就能直接進行登入之後的操作了,而不用進行重複登入。

  2、第二種方法是根據客戶端的IP地址劃分,每次都將同一個 IP 地址傳送的請求都分發到同一個 Tomcat 伺服器,那麼也不會存在 session 共享的問題。

  而 nginx 的基於 IP 路由負載的機制就是上訴第二種形式。大概配置如下:

 1     upstream OrdinaryPolling {
 2     ip_hash;
 3     server 127.0.0.1:8080 weight=5;
 4     server 127.0.0.1:8081 weight=2;
 5     }
 6     server {
 7         listen       80;
 8         server_name  localhost;
 9 
10         location / {
11             proxy_pass http://OrdinaryPolling;
12             index  index.html index.htm index.jsp;
13         
14         }
15     }

  注意:我們在 upstream 指令塊中增加了 ip_hash 指令。該指令就是告訴 nginx 伺服器,同一個 IP 地址客戶端傳送的請求都將分發到同一個 Tomcat 伺服器進行處理。

④、基於伺服器響應時間負載分配

  根據伺服器處理請求的時間來進行負載,處理請求越快,也就是響應時間越短的優先分配。

 1     upstream OrdinaryPolling {
 2     server 127.0.0.1:8080 weight=5;
 3     server 127.0.0.1:8081 weight=2;
 4     fair;
 5     }
 6     server {
 7         listen       80;
 8         server_name  localhost;
 9 
10         location / {
11             proxy_pass http://OrdinaryPolling;
12             index  index.html index.htm index.jsp;
13         
14         }
15     }

  通過增加了 fair 指令。

⑤、對不同域名實現負載均衡

   通過配合location 指令塊我們還可以實現對不同域名實現負載均衡。

 1     upstream wordbackend {
 2     server 127.0.0.1:8080;
 3     server 127.0.0.1:8081;
 4     }
 5 
 6     upstream pptbackend {
 7     server 127.0.0.1:8082;
 8     server 127.0.0.1:8083;
 9     }
10 
11     server {
12         listen       80;
13         server_name  localhost;
14 
15         location /word/ {
16             proxy_pass http://wordbackend;
17             index  index.html index.htm index.jsp;
18         
19         }
20     location /ppt/ {
21             proxy_pass http://pptbackend;
22             index  index.html index.htm index.jsp;
23         
24         }
25     }