本文目錄:1.簡介2.haproxy的特性(1):連接保持和連接關閉3.haproxy的特性(2):會話保持 3.1 源地址hash演算法實現會話保持 3.2 cookie實現會話保持 3.3 stick table實現會話粘性 3.4 session共用4.haproxy的特性(3):後端健康狀況檢 ...
本文目錄:
1.簡介
2.haproxy的特性(1):連接保持和連接關閉
3.haproxy的特性(2):會話保持
3.1 源地址hash演算法實現會話保持
3.2 cookie實現會話保持
3.3 stick table實現會話粘性
3.4 session共用
4.haproxy的特性(3):後端健康狀況檢查和被檢查
5.haproxy的特性(4):處理請求和響應報文
6.haproxy的特性(5):狀態查看
7.haproxy的特性(6):ACL
8.haproxy的特性(7):連接重用功能
1.簡介
haproxy是一款負載均衡軟體,它工作在7層模型上,可以分析數據包中的應用層協議,並按規則進行負載。通常這類7層負載工具也稱為反向代理軟體,nginx是另一款著名的反向代理軟體。
haproxy支持使用splice()系統調用,它可以將數據在兩個套接字之間在內核空間直接使用管道進行傳遞,無需再在kernel buffer-->app buffer-->kernel
之間來回覆制複製數據,實現零複製轉發(Zero-copy forwarding),還可以實現零複製啟動(zero-starting)。haproxy預設對客戶端的請求和對服務端的響應數據都開啟了splice功能,它自身對數據狀態進行判斷,決定此數據是否啟用splice()進行管道傳遞,這能極大提高性能。
2.haproxy的特性(1):連接保持和連接關閉
先說明說明HTTP協議事務模型。
http協議是事務驅動的,意味著每個request產生且僅產生一個response。客戶端發送請求時,將建立一個從客戶端到服務端的TCP連接,客戶端發送的每一個request都經過此連接傳送給服務端,然後服務端發出response報文。隨後這個TCP連接將關閉,下一個request將重新打開一個tcp連接進行傳送。
[conn1][req1]......[resp1][close1][conn2][req2]......[resp2][close2]......
這種模式稱為"http close"模式。這種模式下,有多少個http事務就有多少個連接,且每發出一個response就關閉一次tcp連接。這種情況下,客戶端不知道response中body的長度。
如果"http close"可以避免"tcp連接隨response而關閉",那麼它的性能就可以得到一定程度的提升,因為頻繁建立和關閉tcp連接消耗的資源和時間是較大的。
那麼如何進行提升?在server端發送response時給出content-length
的標記,讓客戶端知道還有多少內容沒有接收到,沒有接收完則tcp連接不關閉。這種模式稱為"keep-alive"。
[conn][req1]...[resp1][req2]...[resp2][close]
另一種提升"http close"的方式是"pipelining"模式。它仍然使用"keep-alive"模式,但是客戶端不需要等待收到服務端的response後才發送後續的request。這在請求一個含有大量圖片的頁面時很有用。這種模式類似於累積報文數量成一批或完成後才一次性發送,能很好的提升性能。
[conn][req1][req2]...[resp1][resp2][close]...
很多http代理不支持pipelining,因為它們無法將response和相應的request在一個http協議中聯繫起來,而haproxy可以在pipelinign模式下對報文進行重組。
預設haproxy操作在keep-alive模式:對於每一個tcp連接,它處理每一個request和response,並且在發送response後連接兩端都處於空閑狀態一段時間,如果該連接的客戶端發起新的request,則繼續使用此連接。
haproxy支持5種連接模式:
keep alive
:分析並處理所有的request和response(預設),後端為靜態或緩存伺服器建議使用此模式。tunnel
:僅分析處理第一個request和response,剩餘所有內容不進行任何分析直接轉發。1.5版本之前此為預設,現在不建議設置為此模式。passive close
:在請求和響應首部加上"connection:close"標記的tunnel,在處理完第一個request和response後嘗試關閉兩端連接。server close
:處理完第一個response後關閉和server端的連接,但和客戶端的連接仍然保持,後端為動態應用程式伺服器組建議使用此模式。forced close
:傳輸完一個response後客戶端和服務端都關閉連接。
3.haproxy的特性(2):會話保持
任何一個反向代理軟體,都必須具備這個基本的功能。這主要針對後端是應用伺服器的情況,如果後端是靜態伺服器或緩存伺服器,無需實現會話保持,因為它們是"無狀態"的。
如果反向代理的後端提供的是"有狀態"的服務或協議時,必須保證請求過一次的客戶端能被引導到同義服務端上。只有這樣,服務端才能知道這個客戶端是它曾經處理過的,能查到並獲取到和該客戶端對應的上下文環境(session上下文),有了這個session環境才能繼續為該客戶端提供後續的服務。
如果不太理解,簡單舉個例子。客戶端A向服務端B請求將C商品加入它的賬戶購物車,加入成功後,服務端B會在某個緩存中記錄下客戶端A和它的商品C,這個緩存的內容就是session上下文環境。而識別客戶端的方式一般是設置session ID(如PHPSESSID、JSESSIONID),並將其作為cookie的內容交給客戶端。客戶端A再次請求的時候(比如將購物車中的商品下訂單)只要攜帶這個cookie,服務端B就可以從中獲取到session ID並找到屬於客戶端A的緩存內容,也就可以繼續執行下訂單部分的代碼。
假如這時使用負載均衡軟體對客戶端的請求進行負載,如果這個負載軟體只是簡單地進行負載轉發,就無法保證將客戶端A引導到服務端B,可能會引導到服務端X、服務端Y,但是X、Y上並沒有緩存和客戶端A對應的session內容,當然也無法為客戶端A下訂單。
因此,反向代理軟體必須具備將客戶端和服務端"綁定"的功能,也就是所謂的提供會話保持,讓客戶端A後續的請求一定轉發到服務端B上。
3.1 源地址hash演算法實現會話保持
作為負載均衡軟體,一般都會提供一種稱為"源地址hash"的調度演算法,將客戶端的IP地址結合後端伺服器數量和權重做散列計算,每次客戶端請求時都會進行同樣的hash計算,這樣同一客戶端總能得到相同的hash值,也就能調度到同一個服務端上。
一般來說,除非無路可選,都不應該選擇類似源地址hash這樣的演算法。因為只要後端伺服器的權重發生任何一點改變,所有源IP地址的hash值幾乎都會改變,這是非常大的動蕩。
3.2 cookie實現會話保持
作為反向代理軟體,一般還提供一種cookie綁定的功能實現會話保持。反向代理軟體為客戶端A單獨生成一個cookie1,或者直接修改應用伺服器為客戶端設置的cookie2,最後將cookie通過在響應報文中設置"Set-Cookie"欄位響應給客戶端。與此同時,反向代理軟體會在記憶體中維持一張cookie表,這張表記錄了cookie1或修改後的cookie2對應的服務端。只要客戶端請求報文中的"Cookie"欄位中攜帶了cookie1或cookie2屬性的請求到達反向代理軟體時,反向代理軟體根據cookie表就能檢索到對應的服務端是誰。
需要註意的是,客戶端收到的cookie可能來源有兩類:一類是反向代理軟體增加的,這時客戶端收到的響應報文中將至少有兩個"Set-Cookie"欄位,其中一個是反代軟體的,其他是應用伺服器設置的;一類是反向代理軟體在應用伺服器設置的Cookie基礎上修改或增加屬性。
例如,當配置haproxy插入cookie時,客戶端從第一次請求到第二次請求被後端應用程式處理的過程大致如下圖所示:
3.3 stick table實現會話粘性
haproxy還提供另一種stick table功能實現會話粘性(stickiness)。這張stick-table表非常強大,它可以根據抽取客戶端請求報文中的內容或者源IP地址或者抽取響應報文中的內容(例如應用伺服器設置的Session ID這個cookie)作為這張表的key,將後端伺服器的標識符ID作為key對應的value。只要客戶端再次請求,haproxy就能對請求進行匹配(match),無論是源IP還是cookie亦或是其它字元串作為key,總能匹配到對應的記錄,而且匹配速度極快,再根據value轉發給對應的後端伺服器。
例如,下圖是一張最簡單的stick table示意圖:
該stick-table存儲的key是客戶端的源IP地址,當客戶端第一次請求到達haproxy後,haproxy根據調度演算法為其分配一個後端appserver1,當請求轉發到達後端後,haproxy立即在stick table表中建立一條ip1和appserver1的粘性(stickiness)記錄。之後,無論是否使用cookie,haproxy都能為該客戶端找到它對應的後端伺服器。
stick table的強大遠不止會話粘性。還可以根據需要定製要記錄的計數器和速率統計器,例如在一個時間段內總共流入了多少個連接、平均每秒流入多少個連接、流入流出的位元組數和平均速率、建立會話的數量和平均速率等。
更強大的是,stick table可以在"主主模型"下進行stick記錄複製(replication),它不像session複製(copy),節點一多,無論是節點還是網路帶寬的壓力都會暴增。haproxy每次推送的是stick table中的一條記錄,而不是整表整表地複製,而且每條記錄占用的空間很小(最小時每條記錄50位元組),使得即使在非常繁忙的情況下,在幾十臺haproxy節點之間複製都不會占用太多網路帶寬。藉助stick table的複製,可以完完整整地實現haproxy"主主模型",保證所有粘性信息都不會丟失,從而保證haproxy節點down掉也不會讓客戶端和對應的服務端失去聯繫。
3.4 session共用
無論反向代理軟體實現的會話保持能力有多強,功能有多多,只要後端是應用伺服器,就一定是"有狀態"的。有狀態對於某些業務邏輯來說是必不可少的,但對架構的伸縮和高可用帶來了不便。我們無法在架構中隨意添加新的代理節點,甚至無法隨意添加新的應用伺服器,高可用的時候還必須考慮狀態或者某些緩存內容是否會丟失。
如果將所有應用伺服器的session信息全部存儲到一臺伺服器上(一般放在redis或資料庫中)進行共用,每台應用伺服器在需要獲取上下文的時候從這台伺服器上取,那麼應用伺服器在取session消息之前就是"無狀態"的。
例如,下麵是一個後端使用session共用的示意圖:
使用session share後,調度器無論將請求調度到哪個後端上,這個後端都能從session share伺服器上獲取到對應的session上下文。這樣無狀態的請求完全可以被任意負載,負載軟體無需記住後端伺服器,從而達到四層負載的效果。如果沒有特殊需求(如處理7層協議),這時可以使用LVS替代haproxy,因為在負載性能上,LVS比haproxy高好幾個級別。
session共用給架構帶來的好處非常多,正如上面所說的,可以使用LVS進行極其高效的負載(前提是沒有LVS無法實現的需求),無論是負載節點還是應用伺服器節點都可以隨意增刪伺服器。而唯一需要保證的就是session共用伺服器的高可用。
4.haproxy特性(3):後端健康狀況檢查和被檢查
任何一個負載均衡軟體,都應該提供後端伺服器健康狀況檢查的功能,即使自身沒有,也必須能夠藉助其他第三方工具來實現。只有具備後端健康檢查的功能,在後端某伺服器down掉的時候,調度器才能將它從後端伺服器組中踢出去,保證客戶端的請求不會被調度到這台down掉的伺服器上。
haproxy為多種協議類型提供了健康狀況檢查的功能,除了最基本的基於tcp的檢查,據我從官方手冊上根據關鍵詞的統計,還為以下幾種協議提供健康檢查:
- HTTP
- ldap
- mysql
- pgsql
- redis
- SPOP
- smtp
如果haproxy沒有指定基於哪種協議進行檢查,預設會使用tcp協議進行檢查,這種檢查的健康判斷方式就是能否連上後端。例如:
backend static_group
server staticsrv1 192.168.100.62:80 check rise 1
server staticsrv2 192.168.100.63:80 check rise 1
在server指令中的check設置的是是否開啟健康檢查功能,以及檢查的時間間隔、判斷多少次不健康後就認為後端下線了以及成功多少次後認為後端重新上線了。
如果要基於其它協議檢查,需要使用協議對應的option指令顯式指定要檢查的對象。且前提是server中必須指定check,這是控制檢查與否的開關。例如,基於http協議檢查:
backend dynamic_group
option httpchk GET /index.php
server appsrv1 192.168.100.60:80 check
server appsrv2 192.168.100.61:80 check
對於基於http協議的檢查,haproxy提供了多種判斷健康與否的方式,可以通過返回狀態碼或拿狀態碼來進行正則匹配、通過判斷響應體是否包含某個字元串或者對響應體進行正則匹配。
例如:
backend dynamic_group1
option httpchk GET /index.php
http-check expect status 200
server appsrv1 192.168.100.60:80 check
server appsrv2 192.168.100.61:80 check
backend dynamic_group2
option httpchk GET /index.php
http-check expect ! string error
server appsrv1 192.168.100.60:80 check
server appsrv2 192.168.100.61:80 check
上面兩個後端組都指定了使用http協議進行檢查,並分別使用http-check expect
指定了要檢查到狀態碼200、響應體中不包含字元串"error"才認為健康。如果不指定http-check expect
指令,那麼基於http協議檢查的時候,只要狀態碼為2xx或3xx都認為是健康的。
haproxy除了具備檢查後端的能力,還支持被檢查,只需要使用monitor
類的指令即可。所謂被檢查,指的是haproxy可以指定一個檢查自己的指標,自己獲取檢查結果,並將檢查狀態上報給它的前端或高可用軟體,讓它們很容易根據上報的結果(200或503狀態碼)判斷haproxy是否健在。
以下是兩個被檢查的示例:
frontend www
mode http
monitor-uri /haproxy_test
frontend www
mode http
acl site_dead nbsrv(dynamic) lt 2
acl site_dead nbsrv(static) lt 2
monitor-uri /site_alive
monitor fail if site_dead
第一個示例中,"/haproxy_test"是它的前端指定要檢查的路徑,此處haproxy對該uri路徑進行監控,當該路徑正常時,haproxy會告訴前段"HTTP/1.0 200 OK",當不正常時,將"HTTP/1.0 503 Service unavailable"。
第二個示例中,不僅監控了"/site_alive",還監控了後端健康節點的數量。當dynamic或static後端組的健康節點數量少於2時,haproxy立即主動告訴前端"HTTP/1.0 503 Service unavailable",否則返回給前端"HTTP/1.0 200 OK"。
5.haproxy的特性(4):處理請求和響應報文
一個合格的反向代理軟體,必須能夠處理流入的請求報文和流出的響應報文。具備這些能力後,不僅可以按照需求改造報文,還能篩選報文,防止被惡意攻擊。
haproxy提供了很多處理請求、響應報文的功能性指令,還有一些所謂"函數"。
大多數處理請求報文的函數都以"req"或"capture.req."開頭,處理響應報文的函數都以"res."或"capture.res."開頭,這樣的函數非常多,幾乎可以實現任何想達到的功能。完整的指令集見官方手冊:https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#7.3.2。
以下是幾個比較具有代表性的函數或指令:
capture request header:捕獲請求報文。
capture response header:捕獲響應報文。
reqadd:在請求首部添加欄位。
rspadd:在響應首部添加欄位。
req.cook(name):獲取Cookie欄位中的name屬性的值。
res.cook(name):獲取"Set-Cookie"欄位中name屬性的值。
。。。。。。
以上函數都是對7層協議進行處理。除此之外,haproxy還有非常多的函數可以分別處理4層、5層、6層協議。
6.haproxy的特性(5):狀態查看
作為反向代理,必須具備查看自身和後端伺服器的狀態信息。
haproxy提供了多種獲取狀態信息的方法:
- 使用
stats enable
指令啟用狀態報告功能,這樣就可以在瀏覽器中輸入特定的url訪問狀態信息。 - 提供了很多對前段狀態和後端節點狀態取樣調查的函數。例如某指定後端或所有後端有多少個節點存活、某後端或所有後端已建立多少連接、後端還有多少連接槽位可以繼續提供連接、前段建立了多少連接等等。指令集合參見官方手冊:https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#7.3.2
- 提供了套接字狀態查看、管理功能。也許很多人都不知道,預設配置文件中的
stat socket
指令是幹嗎用的,其實這就是為系統管理員提供的介面。global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 2000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats
我們安裝"socat"包(socket cat,在epel源提供該包)後,就可以通過socat命令來查看/var/lib/haproxy/stats這個狀態套接字。例如,執行下麵的命令可以獲取到所有可執行的命令。
echo "help" | socat unix:/var/lib/haproxy/stats
例如,其中一條命令是"show backend"用來列出所有的backend,可以這樣使用:
echo "show backend" | socat unix:/var/lib/haproxy/stats
或者也可以進入互動式操作模式:
socat readline unix:/var/lib/haproxy/stats
7.haproxy的特性(6):ACL
可以說,支持ACL的軟體都是好軟體,比如haproxy、varnish。
ACL本意是access control list(訪問控制列表),用來定義一組黑名單或白名單。但顯然,它絕不僅僅是為了黑白名單而存在的,有了ACL,可以隨意按條件定製一組或多組列表。ACL存在的意義,就像是正則表達式存在的意義一樣,極大程度上簡化了軟體在管理上的複雜度。
在haproxy中,只要能在邏輯意義上進行分組的,幾乎都可以使用ACL來定製。比如哪些IP屬於A組,後端哪些節點是靜態組,後端節點少於幾個時屬於dead狀態等等。
8.haproxy的特性(7):連接重用功能
haproxy支持後端連接重用的功能。
在預設情況下(不使用連接重用),當某客戶端的請求到來後,haproxy為了將請求轉發給後端,會和後端某伺服器建立一個TCP連接,並將請求調度到該伺服器上,該客戶端後續的請求也會通過該TCP連接轉發給後端(假設沒有採用關閉後端連接的http事務模型)。但在響應後和該客戶端的下一個請求到來前,這個連接是空閑的。
其實仔細想想,和後端建立的TCP連接僅僅只是為了調度轉發,免去後續再次建立tcp連接的消耗。完全可以為其它客戶端的請求調度也使用這個TCP連接,保證TCP連接資源不浪費。可以使用http-reuse strategy_name
指令設置連接重用的策略,而預設策略禁用連接重用。
該指令有4個值:
- (1).
never
:這是預設設置。表示禁用連接重用,因為老版本的haproxy認為來源不同的請求不應該共用同一個後端連接。 - (2).
safe
:這是建議使用的策略。"安全"策略下,haproxy為客戶端的每個第一個請求都單獨建立一個和後端的TCP連接,但是後續的請求則會重用和該後端的空閑TCP連接。這樣的轉發不僅提高了資源使用率,還保持了keep-alive的功能。因此,safe策略配合http-keep-alive事務模式比http-server-close事務模式更高效,無論後端是靜態、緩存還是動態應用伺服器。 - (3).
aggressive
:一種激進的策略,該策略的haproxy會重用空閑TCP連接來轉發大多數客戶端的第一次請求。之所以是大多數而不是所有,是因為haproxy會挑選那些已經被重用過至少一次的連接(即從建立開始轉發過至少兩次,不管源是否是同一客戶端)進行重用,因為haproxy認為只有這樣的連接才具有重用能力。 - (4).
always
:它將總是為第一個請求重用空閑連接。當後端是緩存伺服器時,這種策略比safe策略的性能要高許多,因為這樣的請求行為都是一樣的,且可以共用同一連接來獲取資源。不過不建議使用這種策略,因為大多數情況下,它和aggressive的性能是一樣的,但是卻帶來了很多風險。
因此,為了性能的提升,將它設置為safe或aggressive吧,同時再將http事務模型設置為http-keep-alive,以免後端連接在響應後立即被關閉。
回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
回到資料庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html
轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/8545723.html
註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!