在上一篇博客咱們介紹了 Nginx 一個很重要的功能——代理,包括正向代理和反向代理。這兩個代理的核心區別是:正向代理代理的是客戶端,而反向代理代理的是服務器。其中咱們又重點介紹了反向代理,以及如何經過 Nginx 來實現反向代理。那麼瞭解了Nginx的反向代理以後,咱們要經過Nginx的反向代理實現另外一個重要功能——負載均衡。html
早期的系統架構,基本上都是以下形式的:nginx
客戶端發送多個請求到服務器,服務器處理請求,有一些可能要與數據庫進行交互,服務器處理完畢後,再將結果返回給客戶端。web
這種架構模式對於早期的系統相對單一,併發請求相對較少的狀況下是比較適合的,成本也低。可是隨着信息數量的不斷增加,訪問量和數據量的飛速增加,以及系統業務的複雜度增長,這種架構會形成服務器相應客戶端的請求日益緩慢,併發量特別大的時候,還容易形成服務器直接崩潰。很明顯這是因爲服務器性能的瓶頸形成的問題,那麼如何解決這種狀況呢?算法
咱們首先想到的多是升級服務器的配置,好比提升CPU執行頻率,加大內存等提升機器的物理性能來解決此問題,可是咱們知道摩爾定律的日益失效,硬件的性能提高已經不能知足日益提高的需求了。最明顯的一個例子,天貓雙十一當天,某個熱銷商品的瞬時訪問量是極其龐大的,那麼相似上面的系統架構,將機器都增長到現有的頂級物理配置,都是不可以知足需求的。那麼怎麼辦呢?數據庫
上面的分析咱們去掉了增長服務器物理配置來解決問題的辦法,也就是說縱向解決問題的辦法行不通了,那麼橫向增長服務器的數量呢?這時候集羣的概念產生了,單個服務器解決不了,咱們增長服務器的數量,而後將請求分發到各個服務器上,將原先請求集中到單個服務器上的狀況改成將請求分發到多個服務器上,將負載分發到不一樣的服務器,也就是咱們所說的負載均衡。瀏覽器
負載均衡完美的解決了單個服務器硬件性能瓶頸的問題,可是隨着而來的如何實現負載均衡呢?客戶端怎麼知道要將請求發送到那個服務器去處理呢?服務器
Nginx 服務器是介於客戶端和服務器之間的中介,經過上一篇博客講解的反向代理的功能,客戶端發送的請求先通過 Nginx ,而後經過 Nginx 將請求根據相應的規則分發到相應的服務器。網絡
主要配置指令爲上一講的 pass_proxy 指令以及 upstream 指令。負載均衡主要經過專門的硬件設備或者軟件算法實現。經過硬件設備實現的負載均衡效果好、效率高、性能穩定,可是成本較高。而經過軟件實現的負載均衡主要依賴於均衡算法的選擇和程序的健壯性。均衡算法又主要分爲兩大類:session
靜態負載均衡算法:主要包括輪詢算法、基於比率的加權輪詢算法或者基於優先級的加權輪詢算法。架構
動態負載均衡算法:主要包括基於任務量的最少鏈接優化算法、基於性能的最快響應優先算法、預測算法及動態性能分配算法等。
靜態負載均衡算法在通常網絡環境下也能表現的比較好,動態負載均衡算法更加適用於複雜的網絡環境。
例子:
這是Nginx 默認的輪詢算法。
例子:兩臺相同的Tomcat服務器,經過 localhost:8080 訪問Tomcat1,經過 localhost:8081訪問Tomcat2,如今咱們要輸入 localhost 這個地址,能夠在這兩個Tomcat服務器之間進行交替訪問。
1、分別修改兩個Tomcat服務器的端口爲8080和8081。而後再修改Tomcat的首頁,使得訪問這兩個頁面時可以區分。以下:
修改端口號文件爲 server.xml :
修改首頁的路徑爲:webapps/ROOT/index.jsp
修改完成以後,分別啓動這兩個Tomcat服務器,而後分別輸入相應的地址端口號:
輸入地址:localhost:8081
輸入地址:localhost:8080
2、修改 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 }
3、啓動 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 端口號出現的次數更多,試驗的次數越多越接近咱們配置的比例。
咱們知道一個請求在通過一個服務器處理時,服務器會保存相關的會話信息,好比session,可是該請求若是第一個服務器沒處理完,經過nginx輪詢到第二個服務器上,那麼這個服務器是沒有會話信息的。
最典型的一個例子:用戶第一次進入一個系統是須要進行登陸身份驗證的,首先將請求跳轉到Tomcat1服務器進行處理,登陸信息是保存在Tomcat1 上的,這時候須要進行別的操做,那麼可能會將請求輪詢到第二個Tomcat2上,那麼因爲Tomcat2 沒有保存會話信息,會覺得該用戶沒有登陸,而後繼續登陸一次,若是有多個服務器,每次第一次訪問都要進行登陸,這顯然是很影響用戶體驗的。
這裏產生的一個問題也就是集羣環境下的 session 共享,如何解決這個問題?
一般由兩種方法:
一、第一種方法是選擇一箇中間件,將登陸信息保存在一箇中間件上,這個中間件能夠爲 Redis 這樣的數據庫。那麼第一次登陸,咱們將session 信息保存在 Redis 中,跳轉到第二個服務器時,咱們能夠先去Redis上查詢是否有登陸信息,若是有,就能直接進行登陸以後的操做了,而不用進行重複登陸。
二、第二種方法是根據客戶端的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 }