高併發與負載均衡——nginx反向代理與負載均衡

一、反向代理

1.面向服務開發

模塊化,解耦的開發,提高團隊合作效率,現階段的開發。

2.正向代理與反響代理的區別

代理是的方向是就客戶端而言的,對於翻牆來說,是正向代理,Client訪問不了服務器,這時候需要一個代理服務器代理我們訪問服務器,這就是正向代理,代理服務器代理客戶端。對於分佈式架構業務服務,來說反向代理服務器其實做的是反響代理的工作,因爲反響代理服務器代理的是Client訪問的後臺服務器而不是Client本身,這就是反向代理。

3.存在的意義

在面向服務編程角度來考慮,則是將整體服務進行解耦,模塊化。即便於開發,也便於維護和擴展。而這最終的體現就是成本的降低。

4.可以做反向代理的服務器:

(1).httpd(tomcat):httpd是可以做反向代理,但是tomcat是一個容器級別服務器,不是一個輕量級,在高併發性能瓶頸上有着不可避免的缺陷,雖然可以通過硬件強行提升,但是對於成本有着非常高的需求。所以httpd不適合做反向代理

(2).nginx:nginx是很適合做反向代理,官方給出的併發量是5w,但是阿里封裝的nginx——>tengine可以達到10w很輕鬆(這是業務需求,硬件匹配,服務器參數調優等多方面因素設置得當纔可以達到)。

5.Nginx和Apache(tomcat)

(1)Nginx和apache的優缺點:

(2)Nginx和apache的模型對比

進程對比:

Nginx是CPU有多少個核心,就有多少個進程,而apache則是一個連接一個進程。那麼在進程切換對cpu和IO是有損耗的,在高併發情況下,apache會開闢非常多進程,對cpu和io有非常大的損耗,進程切換又有着額外開銷,所以這是apache的弊端;而Nginx則是一個cpu有多少核心,就有多少進程,那麼進程數量很少,一個或者兩個進程對應多個客戶端Client,那麼Nginx是如何在單進程面對多Client請求下做到數據發生不發生交互錯誤,又高效處理呢?答案就是異步非阻塞IO。

首先我們要知道網絡IO流的速度是很慢的(ms級別),而CPU的速度是很快的(ns級別)。CPU比IO速度要快100w倍!!!而Apache使用的是同步阻塞IO,也就是在等連接建立完成後,CPU會等待1ms的時間來等待數據包從客戶端傳輸過來。這個1ms的時間被浪費了,CPU被佔用等待了。而Nginx則不是這樣,當連接建立後,Nginx不會讓CPU進行等待,而是讓CPU不斷詢問linux系統中的連接文件,問是否有數據傳輸過來了,如果沒有,則詢問下一個,如果有則掛起別的進程立刻執行有數據的進程。這種輪詢的方式後續又得到了改善,變成一旦這個連接文件有數據傳輸過來,會立刻向CPU發送中斷請求,而CPU會立刻去處理該連接的數據,更大效率提高了IO速度和CPU的使用率。

加載部署:

nginx支持熱加載與熱部署,在nginx服務啓動的時候,依然能夠修改配置文件,讓其重新加載配置文件與繼續服務。tomcat則沒有。nginx這一點得益於它的模塊化開發。

nginx爲什麼能這樣,我們首先要知道一個基礎概念,程序本質上沒有運行的時候,就是一個文件。而當程序被加載到內存中,則纔是程序。這個時候如果我們刪除或者程序的原本文件,是不會影響內存的,因爲內存已經加載到了程序文件。windows不可以這樣是因爲windows在運行程序時,會給程序文件加鎖,如果打開了鎖,windows也可以。linux顯然也可以這樣去做。那對於nginx的熱加載,linux是如何處理的呢?

如下圖所示,nginx啓動後,會開啓兩個進程。一個是manager,一個是worker。manager進程從屬於root,它會讀取nginx.conf來創建一個worker進程,並將它交給nobody權限用戶。所有連接處理和轉發,都是交給worker進程來處理的。一旦發生熱加載,那麼manager進程會重新讀取配置文件,並等待老worker進程處理完所有任務後,創建新worker進程,殺死老worker,從而平滑的不用停止服務器就實現了服務器加載和更新。

 

二、nginx做反向代理搭建實戰

(1)Nginx.conf文件分析與配置

(2)小實驗1:將更改配置文件,實現nginx的虛擬服務器功能

前提:修改windows的host文件,設置ip與域名的映射關係

結果演示:

(3)關於server方法中location字段方法的分析和設置

(4)小實驗2:解決nginx的鏈接跳轉問題

  • 問題描述:我們通過nginx反向代理訪問www.baidu.com,如果我們通過http來訪問,則會發生頁面跳轉問題
    • 現象
      • nginx的location配置
      • 訪問www.wymnode01.com/oxox,發現發生了頁面跳轉,跳轉到www.baidu.com
    • 結果分析:由於Client通過反響代理和http訪問百度,請求也確實經過了nginx到達了百度,但是由於現在的大網站都是https的協議。百度服務器收到http請求後,會給Client返回一個跳轉連接,那麼Client通過跳轉連接則會和百度服務器直接建立連接而跳過nginx。所以我們要避免這種情況
    • 問題解決:在114行將http改成https就可以了

nginx.conf的相關知識還非常非常多,查看視頻和翻閱資料博客,繼續整理

 

 

 

三、高併發

1.用別的可以做反向代理的服務器做反向代理存在高併發的問題

  • 從業務分佈式解決高併發:我們假設存在一種反向代理服務器(A)也可以承擔5w的併發量,並根據業務功能的不同對搭載不同業務的tomcat做服務分發,這從一定角度上解決了高併發的問題,但是隻是從服務業務的不同角度來解決了高併發問題。

  • 從業務分佈式解決不了的高併發:如果客戶端有2000個都是搜索方面的請求,那麼這些流量和請求都會由A轉發給搜索服務器的tomcat,tomcat承受不了2000的併發量,所以會崩潰。這時候就需要在反向代理服務器和搜索服務器tomcat之間搭建負載均衡,並將搜索服務器配置成tomcat集羣。

2.使用nginx做反向代理服務器可以解決負載均衡和反向代理的問題

nginx的特點和可以解決的問題:nginx不僅僅可以做反向代理,還可以做負載均衡,也就是說他是反向代理+LVS。nginx這一特點就可以從業務分佈式角度、單一業務的流量對該業務服務器造成壓力的角度,這兩個角度來解決高併發問題,算是一舉兩得。

3.高併發的體會和理解

高併發問題絕對不是一個從單一角度就可以解決的問題,我們現在所討論的是從服務器架構角度解決高併發問題,這個角度的解決的出發點是客戶端的大量流量會給服務器造成巨大壓力,也會給服務器造成巨大性能損耗,同時也考慮到了I/O速度和數據傳輸流量等問題。高併發的問題會出現在服務器架構和軟件架構的各個分層之間,比如dubbo是解決軟件層面表示層和業務層之間的併發問題,redis則是在業務層和持久層這兩層來解決併發與效率問題,而sql索引和分庫分表和sql語句的書寫則是在持久層這一層解決效率問題。而我們的LVS和nginx則是在網絡流量如何均攤這一層來解決,屬於是在軟件層面外,在操作系統這一層面,網絡傳輸這一層面來解決高併發問題(因爲LVS本身就是linux內核所帶的,nginx工作在應用層也就是要建立TCP連接的)。

 

四、LVS+nginx負載均衡

1.知識補充

nginx工作流程:如圖1所示,也就是1——4的過程,更加說明nginx是要和客戶端建立握手連接的,這點與LVS是不同的!

用戶體驗:互聯網對3秒比較敏感,如果請求的數據能夠在3秒之內發送到,則用戶體驗會很好,這3秒客戶端和RealServer(RS)之間發生的交互首先是客戶端先和nginx建立連接成功,然後nginx在此基礎上對url進行解析並交給後面RS做業務處理,最後RS將結果返回給nginx,nginx再返回給客戶端。整個過程要3秒內完成。

2.LVS+nginx的優點

  • 突破nginx性能瓶頸:nginx是5w,如果併發量是50w呢,這時候則需要部署nginx集羣,並在nginx集羣前部署LVS,nginx數量(10臺)*5w,就可以實現50w的併發量了,如圖1。
  • 改善LVS瞎子負載缺點:LVS由於是4層技術,不能建立TCP連接,也就解析不了URL,那麼就不能根據客戶的實際請求做正確的負載均衡(有時候會碰巧碰到LVS做出了正確的負載均衡,是因爲LVS碰巧把數據包轉發給了正確的RealServer,因爲LVS會保持握手>>數據傳輸>>揮手的原子性,所以後續的數據包如果連接沒有中斷,是會正確的返還給客戶端的,但是如果一開始LVS在握手包建立過程中就將數據包發送給了不相匹配的業務的RealServer,則整個連接就錯誤的),而nginx的反向代理技術則解決了這一點。

圖1

圖2(爲圖1的再簡化抽象)

LVS將客戶端請求發送給lvs後的所有鏡像一致的web server。webserver爲輕量級的nginx,擁有反向代理(轉發)+負載均衡的能力,再將LVS負載過來的數據包進行負載均衡和轉發,從而實現了業務架構的拆解。

3.LVS+nginx的缺點

在之前的LVS:DR模型我們可以知道的是,RealServer可以直接向客戶端返回數據包,解決了網絡流帶寬數據傳輸的瓶頸問題。但是LVS+nginx則不可以,數據包只能由nginx返回。因爲LVS:DR模型我們通過MAC地址欺騙和隱藏VIP等方式可以讓客戶端和RealServer直接建立握手連接,那麼Client——RS之間的握手>>數據傳輸>>揮手整個過程是完整獨立的。但是LVS+nginx的負載均衡模型中,客戶端是和nginx之間建立握手連接,nginx和RS之間建立握手連接,那麼客戶端沒有和RS建立TCP連接,則RS不能直接將數據發送給客戶端,只能轉交給nginx,由nginx發送給客戶端,那麼流量的限制就體現在nginx服務器的個數和端口上了,而不是nginx後面數以萬計RS身上了。

 

三、Nginx做負載均衡搭建實戰

 

實驗一、nginx做負載均衡

1.vi /usr/local/nginx/conf/nginx.conf  ———>進入nginx.conf文件中

2.添加location字段,讓nginx對匹配的url做轉發,這裏配置在了63——68行

3.將keepalive_timeout 0打開,將keepalive_timeout 65註釋掉,原因在腳本註釋中寫的很清楚

4.添加upstream方法,方法名稱隨便,裏面添加服務器列表,表示nginx可以將請求轉發給那些服務器。

5.測試

刷新

 

實驗二、:nginx給兩臺tomcat的RealServer做負載均衡,返回Session不同並解決Session一致性的問題

  • 問題描述:假設nginx後面RealServer集羣(R1和R2)搭載tomcat,並有着用戶登錄和用戶主頁訪問的功能,那麼如果我們的Client先訪問R1並登錄獲取了R1發送的Session,這時候連接斷開,Client攜帶Session訪問RealServer集羣,但是nginx將這個請求負載給了R2,由於R2的Session和R1的Session不一樣,就會引發登錄重定向,要求用戶重新登陸。這樣的情況和京東淘寶的登陸情況是不同的,因爲兩個網站都沒有需要重複登陸的問題,在解決問題之前我們先將問題復現
  • 問題復現
    • 將R1與R2(node02和node03)裝上,並進入tomcat的主頁文件進行修改,將之前的主頁index.jsp備份成爲index.jsp.bak,對主頁進行如下修改
      • node02
      • node03
    •  在node01也就是nginx.conf中給R1和R2配置負載均衡,並監聽8080端口,防火牆也放開8080端口
      • 添加upstream
      • 添加location
    • 結果演示:兩者session不同
      • 負載給node02
      • 刷新後負載給node03
    • 問題解決session不同的問題我們可以在RealServer集羣后加一臺服務器(S1),讓RealServer集羣將Session統一寫到S1中,即使負載均衡給了不同的RealServer,那麼這臺RealServer只需要查詢S1的Session表中是否有對應的Session,不需要和自己產生的Session進行匹配了。但是問題又會出現,這臺S1會承載大量的RealServer的IO請求,如果S1是一臺mysql,則會非常慢,因爲IO速度成爲了限制瓶頸(硬盤IO速度很慢)。我們可以使用Redis集羣來解決這個問題,將IO速度提升爲內存IO速度,從而提升了性能。我們這裏先不用Redis,先用memcached來實現,後續引出Redis。因爲session不一致問題不是出現在nginx,所以我們只需要將緩存服務器搭建在tomcat的集羣之外的服務器就可以了,這裏我們選擇搭建在nginx服務器上。
      • 解決步驟:
        • 安裝memcahed:這裏下圖的藍色IP就是nginx服務器的IP,我改成了我的192.168.88.123
      • 啓動memcached,如上圖紅色指令
      • 修改RealServer集羣中tomcat文件夾下的context.xml文件,讓tomcat將Session寫到memcached服務器中,只需要在Context字段下添加Manager字段
      • 給RealServer集羣添加響應的jar包,添加到tomact的lib目錄下,也就是Mananger字段中的所提到的jar包
      • 結果演示
        • node02
        • node03
        • 遺留問題:這裏應該刷新過後session是不變的,但是還是發生了變化,沒有導入jar包會報404錯誤,導入jar包後不會出現404錯誤但是出現了session變化問題,具體原因還有待商榷,初步判定應該是沒有導入正確版本的jar包。
          • ​​​​​​​遺留問題解決:
            • ​​​​​​​1.首先考慮到的是jar包版本問題,重新翻閱博客找到了新的jar包,但是還是出現了session變化問題。所以
            • 2.後來考慮到了RealServer的集羣時間,nginx的服務器時間以及客戶端時間是否是一致的問題,因爲如果客戶端時間或者nginx時間比RealServer的時間早,那麼RealServer會判定這個session已經過期了。就會發送新Session,那麼就會出現session變化,但是發現nginx服務器的時間不會影響結果。但是還是要保證當前項目下服務器的時間一定要是一致的。
            • 3.之後懷疑是否是session本身存活時間過短,但是查看tomcat的server.xml發現session存活時間默認是30分鐘,所以排除
            • 4.最終原因memcached的端口11211沒有打開,最後發現是這個原因,也就明白了,因爲session結果是從memcached中查詢得到的,那麼如果沒有打開11211端口,RealServer就不能對memcached進行讀寫操作,那麼一旦發生負載均衡出現Client拿着R1的Session去訪問R2,RealServer不能訪問memcached,只能自己生成新Session給Client。也就發生了即使配置了memcached,也會存在session不一致的情況
              • ​​​​​​​解決後結果演示
                • node02​​​​​​​
                • 刷新後node03
        • 補充點:集羣時間很重要,一個集羣的機器的時間一定要設置成爲一樣的。不然session會出現過期的現象,這樣服務器就會重新創建一個session,那又會出現之前的重複登陸問題了!!

四、LVS和nginx的缺陷——keepalived的引出

1.問題

  • 1.LVS或者nginx的單點故障問題,會導致後面整個RealServer集羣無法訪問
  • 2.RealServer的單點故障,會造成一部分客戶端的不可用

​​​​​​​2.解決——主從配置

(1)解決LVS單點故障

給當臺LVS配置多個從屬服務器,主服務器會給從屬服務器定期廣播一個數據包,來證明它還活着。一旦主服務器沒有在規定時間點發送數據包,從屬服務器不會立刻判定主服務器死亡,而是會再次等待主服務器發送廣播數據包(這樣做是爲了防止網絡延遲和IP衝撞,丟包等問題造成的主服務器還活着,但是發送的廣播數據包從屬服務器並沒有接受到)。一旦確定主服務器死亡,則從屬服務器會選舉出一個主服務器來繼續工作,並將掛掉的過去的主服務器的IP地址VIP繼承過來——這就是IP漂移

(2)解決RealServer的單點故障

我們會監控RealServer的健康狀態通過shell腳本來讓LVS對RealServer做心跳檢測),一旦RealServer出現故障,則在LVS的負載表中暫時刪除掉故障的RealServer的IP,這樣就可以保證客戶端訪問不到故障的RealServer,同時對RealServer做修復,以及LVS可以試探去訪問RealServer來驗證是否修復成功。等修復成功後再添加到LVS的負載表中。

  1. 如何驗證檢測RealServer是健康狀態

需要注意的是,我們要驗證RealServer是否存活,本質上是驗證RealServer上的搭載我們服務的服務器軟件是否運行正常——也就是tomcat,這是對網絡層第7層的驗證我們如果通過linux的ping指令來驗證是不行的,因爲ping指令驗證的是第三層IP層,連握手都沒有建立。所以我們可以寫shell腳本來讓LVS給RealServer發送http請求,查看tomcat返回的服務器狀態碼,如果是200則健康。我們也可以使用keepalived來做這件事情。

 

 

 

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