【前言】部署服務器用到了nginx,相比較於apache並發能力更強,優點也比其多得多。雖然我的項目可能用不到這么多性能,還是部署一個流行的服務器吧!
此篇博文主要學習nginx(ingine x)的反向代理、負載均衡原理,並介紹一下分布式下sesssion保持。(分布式和集群的區別?下面有)
一、正向代理和反向代理
1、正向代理的概念
正向代理,也就是傳說中的代理,他的工作原理就像一個跳板,簡單的說,我是一個用戶,我訪問不了某網站,但是我能訪問一個代理服務器。這個代理服務器呢,他能訪問那個我不能訪問的網站於是我先連上代理服務器,告訴他我需要那個無法訪問網站的內容,代理服務器去取回來,然后返回給我。從網站的角度,只在代理服務器來取內容的時候有一次記錄,有時候並不知道是用戶的請求,也隱藏了用戶的資料,這取決於代理告不告訴網站。
結論就是 正向代理 是一個位於客戶端和原始服務器(origin server)之間的服務器,為了從原始服務器取得內容,客戶端向代理發送一個請求並指定目標(原始服務器),然后代理向原始服務器轉交請求並將獲得的內容返回給客戶端。客戶端必須要進行一些特別的設置才能使用正向代理。
2、反向代理的概念
繼續舉例:
例用戶訪問 http://ooxx.me/readme,但ooxx.me上並不存在readme頁面,他是偷偷從另外一台服務器上取回來,然后作為自己的內容吐給用戶,但用戶並不知情。這很正常,用戶一般都很笨。這里所提到的 ooxx.me 這個域名對應的服務器就設置了反向代理功能。
結論就是 反向代理正好相反,對於客戶端而言它就像是原始服務器,並且客戶端不需要進行任何特別的設置。客戶端向反向代理 的命名空間(name-space)中的內容發送普通請求,接着反向代理將判斷向何處(原始服務器)轉交請求,並將獲得的內容返回給客戶端,就像這些內容 原本就是它自己的一樣。
3、兩者區別
從用途 上來講:
正向代理的典型用途是為在防火牆內的局域網客戶端提供訪問Internet的途徑;正向代理還可以使用緩沖特性減少網絡使用率。
反向代理的典型用途是將 防火牆后面的服務器提供給Internet用戶訪問;反向代理還可以為后端的多台服務器提供負載平衡,或為后端較慢的服務器提供緩沖服務。
另外,反向代理還可以啟用高級URL策略和管理技術,從而使處於不同web服務器系統的web頁面同時存在於同一個URL空間下。
從安全性 來講:
正向代理允許客戶端通過它訪問任意網站並且隱藏客戶端自身,因此你必須采取安全措施以確保僅為經過授權的客戶端提供服務;
反向代理對外都是透明的,訪問者並不知道自己訪問的是一個代理。
二、nginx的反向代理負載均衡

Nginx服務器就是反向代理服務器,只做請求的轉發,后台有多個http服務器提供服務,nginx的功能就是把請求轉發給后面的服務器,並把結果返回給客戶端。實現在同一個域名之下,有多台服務器,並實現服務器負載均衡。
1、什么是負載均衡?
負載均衡,英文名稱為Load Balance,其意思就是算力分攤到多個操作單元上進行執行,例如Web服務器、FTP服務器、企業關鍵應用服務器和其它關鍵任務服務器等,從而共同完成工作任務。
負載均衡建立在現有網絡結構之上,它提供了一種廉價有效透明的方法擴展網絡設備和服務器的帶寬、增加吞吐量、加強網絡數據處理能力、提高網絡的靈活性和可用性。負載均衡,核心就是網絡流量分發,分很多維度。從網絡層看,基本是四層(TCP,UDP),和七層(HTTP,HTTPS等),基本就是解析到對應的網絡層,然后根據不同特征分發。比如四層的,基本就是根據連接信息(TCP)或者本身的特征(源IP,目標IP)等做。七層的,就可以用域名(HTTP頭里的Host),URL,Cookie,Header這些來做。
2、負載均衡的分類
(1)從實現上看,基本可以分軟負載均衡和反向代理:
- 軟負載均衡不會過中間代理,網絡rt,性能會較好,但是一般不好做精細的流量控制,有的方案還有延時問題。實現有DNS實現,iptables實現的方案。
- 反向代理,故名思意就是通過代理來做嗎,有中間件。由於流量都會過LB,因此可以做到比較精細的流量分發(比如各種權重,七層的各種轉發規則)。壞處就是代理本身可能成為瓶頸,以及過了一層代理造成網絡延時的增加。而代理本身也會有一定成本,因此實現成本較高。
- DNS負載均衡可以根據地域就近分配服務器,但比較簡單使用簡單輪詢方式。有時候可搭配反向代理服務器在集群中使用,因為在客戶端和反向代理服務器之間,可以對多個反向代理服務器進行負載均衡。
- 一個小結
(2)從實現方式看,有軟件負載均衡和硬件負載均衡:
- 軟件負載均衡的具體軟件實現有Nginx,Haproxy,LVS,以及比較古老的Apache等,上面兩種但是軟件負載均衡。現在比較新的做法是用dpdk這種內核bypass方案做的負載均衡,由於繞過了linux內核比較復雜的 網絡協議棧(人家本身就不是做負載均衡的。。。),因此性能會有明顯的提升(輕松跑滿萬兆網卡)。
- 硬件負載均衡有大名鼎鼎的F5之類,這種不差錢的企業會采用。但是現在互聯網公司用的越來越少。現在硬件使用較多的是使用支持OSPF協議的交換機(基本都支持了),通過ECMP做的負載均衡集群。這個查查雲計算廠商的負載均衡文檔,大部分都是用這個作為分發集群的。性能非常好(隨便幾十G器),穩定性也很高。就是貴,搭建麻煩(需要機房支持),所以不是一般用戶搞的起的。不過這個也可以用軟件路由器(比如quagga這種)自己搭建一套軟件的OSPF集群,不過穩定性和性能相比硬件的要大打折扣。
每個雲服務都會提供負載均衡服務(負載均衡服務_流量分發服務-網易雲),拿來直接用就好啦,省時又省力,可以將更多精力放在核心業務上面。
3、反向代理nginx負載均衡舉例說明
nginx作為負載均衡服務器,用戶請求先到達nginx,再由nginx根據負載配置將請求轉發至tomcat服務器。
nginx負載均衡服務器:192.168.3.43
tomcat1服務器:192.168.3.43:8080
tomcat2服務器:192.168.3.43:8081
4、nginx配置
upstream tomcatserver1 {
server 192.168.3.43:8080 weight=2;
server 192.168.3.43:8082 weight=1; #多加了此台服務器,並增加了負載的權重。性能高的服務器做的事情多
}
upstream tomcatserver2 {
server 192.168.3.43:8082;
}
#第一台服務器
server {
listen 80;
server_name 8080.zcinfo.com;
location / {
proxy_pass http://tomcatserver1;
index index.html index.htm;
}
}
#第二台服務器
server {
listen 80;
server_name 8082.zcinfo.com;
location / {
proxy_pass http://tomcatserver2;
index index.html index.htm;
}
}
這樣的情況下,就反向代理成功了。會出現兩個端口下的頁面輪詢。那么負載均衡體現在哪里呢?
5、負載均衡的策略
上面的weight大小就是一種負載均衡的策略,weight=2的會連續出現兩次,1的出現一次。總的來說,nginx提供五種負載均衡的策略。
(1)輪詢(默認)
每個請求按時間順序逐一分配到不同的后端服務器,如果后端服務器down掉,能自動剔除。
upstream backserver {
server 192.168.0.14;
server 192.168.0.15;
}
(2)指定權重
指定輪詢幾率,weight和訪問比率成正比,用於后端服務器性能不均的情況。 默認為1
upstream backserver {
server 192.168.0.14 weight=3;
server 192.168.0.15 weight=10;
}
(3)IP綁定 ip_hash
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決session的問題。因為對於集群問題不好解決session問題。
upstream backserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
(4)fair(第三方)
按后端服務器的響應時間來分配請求,響應時間短的優先分配。
upstream backserver {
server server1;
server server2;
fair;
}
(5)url_hash(第三方)
按訪問url的hash結果來分配請求,使每個url定向到同一個后端服務器,后端服務器為緩存時比較有效。
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
6 、在需要使用負載均衡的server中增加 :
proxy_pass http://backserver/;
upstream backserver{
ip_hash;
server 127.0.0.1:9090 down; #(down 表示單前的server暫時不參與負載)
server 127.0.0.1:8080 weight=2; # (weight 默認為1.weight越大,負載的權重就越大)
server 127.0.0.1:6060;
server 127.0.0.1:7070 backup; # (其它所有的非backup機器down或者忙的時候,請求backup機器)
}
max_fails :允許請求失敗的次數默認為1.當超過最大次數時,返回proxy_next_upstream 模塊定義的錯誤
fail_timeout:max_fails次失敗后,暫停的時間
三、分布式下session保持
負載均衡的應用背景下是多台服務器,那么對於多台服務器這樣進行session保持呢?
我有一篇文章已經介紹了session,此處不再贅述。下面介紹一下session的管理,例如在java服務器下,例如tomcat服務器sessionid會以jsessionid的名稱存在cookie中,以cookie的形式和服務器通信。
1、session怎么保存的?
不同的服務器、不同的語言框架都有不同的實現。比如java的服務器,有的是用文件方式來存儲的;有的是用內存cache的方式來存儲的;有的語言的服務器將數據做加密,然后設置成cookie,存到了客戶端(瀏覽器)。那這些實現方式都有哪些優缺點呢?我們逐個來分析。
-
文件方式:這種方式,將文件作為一個map,當新增一個數據的時候,就在文件中增加類似這樣的一條數據:
angOwberup =>
data={"user":{"id":1,"nickname":"老王"}};
expiry="2016-10-0100:00:00"
(當然,具體實現的時候有可能是用的二進制方式,而不是字符串)
這種方式的好處,就是能夠存儲大量的用戶session,使得這個session有效期可以比較長(比如:三個月用戶不用登錄)。不過這個方式也有對應的問題,就是文件操作比較麻煩。比如,有一個用戶的session過期了,需要刪掉這條記錄,那這個文件就需要挪動或重寫。 -
cache方式:有好多web端的邏輯服務器都采用這種方式。這種方式好處非常明顯,就是實現起來非常簡單。將所有數據放入到內存cache中。如果有失效,直接內存刪除就可以了。不過帶來的問題也很明顯,當服務器重啟以后,所有session都丟失了。或者當有大量用戶登錄(也有可能是遭受攻擊),就會很快讓cache被充滿,然后大量session被LRU算法淘汰,造成session的大量失效,使得用戶需要反復登錄等操作。
- cookie方式:這種方式是最偷懶的方式,也是我上面提的爬蟲遇到的。就是服務器任何數據都不存,把你們所有的客戶端當做我的存儲器,我就需要做一個加密和解密操作。當然這種方式最大的好處就是實現極其簡單(還有其他的好處,稍后再說),不過問題也是很明顯的,就是客戶端要記錄大量信息,同時還要保證加密信息的安全。如果session里要存放大數據,這種方式就不是很適合了。
除了上述說到的優缺點以外,第一和第二,He兩種方式還有另外一個問題,就是當我有不止一台服務器的時候,不同服務器間的session數據共享就成問題了。比如,最初我只有一台服務器1,他的session里記錄了user-1和user-2的數據。這個時候,我需要增加一台服務器2。當nginx把用戶的請求轉發到服務器2的時候,他就傻眼了:用戶帶了一個jsession_id=angOwberup這個的cookie過來,而在他的session管理器里卻找不到這樣一個session數據。那該怎么辦?!
因此,就出現了我們文章一開始提到的問題:在分布式系統里,用戶session如何才能實現同步?
2、分布式的session同步
(PS:分布式和集群的區別在於分布式解決的是壓力集中問題,每個單元任務都不一樣;集群是解決大項目臃腫問題,是同一個項目分散在不同單元上。所以此處用分布式比較合適)
有了上面的情況,我們就必須要去考慮,如何在多個服務器之間實現session同步這個操作。常見的做法有以下幾種,我們逐個來看看:
- 程間通信傳遞session數據,這是最容易想到的一個方法。我們在不同的server服務里開一個socket,然后用socket來將相互擁有的session數據進行傳遞。我記得多年以前tomcat就是采用這樣的方式來做的(已經很久沒用過tomcat了,不知道現在是否還在這樣使用)。這種方式的好處很明顯,就是原理簡單明了;壞處也很明顯,就是同步合並過程復雜,還容易造成同步延遲。比如,某個用戶在server-1登錄了,server-1存儲了這個用戶的session,當正准備將數據同步給server-2的時候,由於用戶訪問實在是太快(飛一般的速度),server-2還沒收到server-1傳來的session數據,用戶訪問就已經來了。這個時候,server-2就不能識別這個用戶,造成用戶需要再次登錄。而且,當有成千上萬台服務器的時候,session同步就是一個噩夢:每一個服務器都要將自己擁有的session廣播給其他所有機器,而且還要隨時進行,不能停歇…… 這些機器估計都是累死的)
- cookie存儲方式。我們在上面講到了一個很偷懶的方式,就是把session數據做加密,然后存儲到cookie中。用戶請求到了,就直接從cookie讀取,然后做解密。這種方式真是把分布式思想發揮到了一個相當的高度。他把用戶也當做分布式的一員,你要訪問數據,那你就自己攜帶着他,每次到服務器的時候,我們的服務器就只負責解密。對於session里只存放小數據,並且加密做的比較好(防止碰撞做暴力破解)的系統來講,這是一個比較好的選擇。他實現超級簡單,而且不用考慮數據的同步。不過如果要往session里存放大數據的情況就不是太好處理。或者安全性要求很高的系統,也不是太好的一個方式(數據有被破解的風險)。
- cache集群或者數據庫做session管理。我們也可以采用另外一種架構來解決session同步問題,那就是引入統一session接入點。我們session放入到cache集群(一個容易宕掉)或者數據庫中,每次請求的時候,都從他們中來獲取。這樣,所有的機器都能獲取到最新的session數據。這種方案也是很多中大型網站采用的解決方案。他實現起來相對簡單(利用cache集群或者主從數據庫自身的管理來實現多機的互備),而且效率很高,安全性也不錯。(NFS)
- 還有一種方式是從上面這種方式延展出來的,就是提供session服務。這個服務負責管理session,其他服務器每次從這個服務處獲取session數據,從而達到數據的共享。
- redis、memcache同步session。二者可以把web服務器中的內存組合起來,成為一個"內存池",不管是哪個服務器產生的sessoin都可以放到這個"內存池"中,其他的都可以使用。不同之處在於redis會有固化操作。參考:https://www.cnblogs.com/lingshao/p/5580287.html
