微服務實戰(二):微服務的接入層設計與動靜資源隔離


原文鏈接:微服務的接入層設計與動靜資源隔離(來源:劉超的通俗雲計算)

 

這個系列是微服務高並發設計,所以我們先從最外層的接入層入手,看都有什么樣的策略保證高並發。
接入層的架構畫一個簡圖來講包括下面的部分。

1.png


接下來我們依次解析各個部分以及可以做的優化。

一、數據中心之外:DNS、HttpDNS、GSLB

當我們要訪問一個網站的服務的時候,首先訪問的肯定是一個域名,然后由DNS,將域名解析為IP地址。

我們首先先通過DNS訪問數據中心中的對象存儲上的靜態資源為例子,看一看整個過程。

我們建議將例如文件、圖片、視頻、音頻等靜態資源放在對象存儲中,直接通過CDN下發,而非放在服務器上,和動態資源綁定在一起。

假設全國有多個數據中心,托管在多個運營商,每個數據中心三個可用區Available Zone,對象存儲通過跨可用區部署,實現高可用性,在每個數據中心中,都至少部署兩個內部負載均衡器,內部負載均衡器后面對接多個對象存儲的前置服務proxy-server。

2.jpg


1、當一個客戶端要訪問object.yourcompany.com的時候,需要將域名轉換為IP地址進行訪問,所以他要請求本地的resolver幫忙

2、本地的resolver看本地的緩存是否有這個記錄呢?如果有則直接使用

3、如果本地無緩存,則需要請求本地的Name Server

4、本地的Name Server一般部署在客戶的數據中心或者客戶所在的運營商的網絡中,本地Name Server看本地是否有緩存,如果有則返回

5、如果本地沒有,本地Name Server需要從Root Name Server開始查起,Root Name Server會將.com Name Server的地址返回給本地Name Server

6、本地的Name Server接着訪問.com的Name Server,他會將你們公司的yourcompany.com的Name Server給本地Name Server

7、本地的Name Server接着訪問yourcompany.com的Name Server,按說這一步就應該返回真實要訪問的IP地址。

對於不需要做全局負載均衡的簡單應用來講,yourcompany.com的Name Server可以直接將object.yourcompany.com這個域名解析為一個或者多個IP地址,然后客戶端可以通過多個IP地址,進行簡單的輪詢,實現簡單的負載均衡即可。

但是對於復雜的應用,尤其是跨地域跨運營商的大型應用,則需要更加復雜的全局負載均衡機制,因而需要專門的設備或者服務器來做這件事情,這就是GSLB,全局負載均衡器。

從yourcompany.com的Name Server中,一般是通過配置CNAME的方式,給object.yourcompany.com起一個別名,例如object.vip.yourcomany.com,然后告訴本地Name Server,讓他去請求GSLB去解析這個域名,則GSLB就可以在解析這個域名的過程中,通過自己的策略實現負載均衡。

圖中畫了兩層的GSLB,是因為分運營商和分地域,我們希望將屬於不同運營商的客戶,訪問相同運營商機房中的資源,這樣不用跨運營商訪問,有利於提高吞吐量,減少時延。

8、第一層GSLB通過查看請求他的本地Name Server所在的運營商,就知道了用戶所在的運營商,假設是移動,然后通過CNAME的方式,通過另一個別名object.yd.yourcompany.com,告訴本地Name Server去請求第二層的GSLB

9、第二層的GSLB通過查看請求他的本地Name Server所在的地址,就知道了用戶所在的地理位置,然后將距離用戶位置比較近的Region的里面的內部負載均衡SLB的地址共六個返回給本地Name Server

10、本地Name Server將結果返回給resolver

11、resolver將結果緩存后,返回給客戶端

12、客戶端開始訪問屬於相同運營商的距離較近的Region1中的對象存儲,當然客戶端得到了六個IP地址,他可以通過負載均衡的方式,隨機或者輪詢選擇一個可用區進行訪問,對象存儲一般會有三份備份,從而可以實現對存儲讀寫的負載均衡。

從上面的過程可以看出,基於DNS域名的GSLB實現全局的負載均衡,可是現在跨運營商和跨地域的流量調度,但是由於不同運營商的DNS緩存策略不同,會造成GSLB的工作實效。

有的用戶的DNS會將域名解析的請求轉發給其他的運營商的DNS進行解析,導致到GSLB的時候,錯誤的判斷了用戶所在的運營商。

有的運營商的DNS出口會做NAT,導致GSLB判斷錯誤用戶所在的運營商。

所以不同於傳統的DNS,有另一種機制稱為httpDNS,可以在用戶的手機App里面嵌入SDK,通過http的方式訪問一個httpDNS服務器,由於手機App可以精確的獲得自己的IP地址,可以將IP地址傳給httpDNS服務器,httpDNS服務器完全由應用的服務商提供,可以實現完全自主的全網流量調度。

二、數據中心之外:CDN

對於靜態資源來講,其實在真實的訪問機房內的對象存儲之前,在最最接近用戶的地方,可以先通過CDN進行緩存,這也是高並發應用的一個總體的思路,能接近客戶,盡量接近客戶。

CDN廠商的覆蓋范圍往往更廣,在每個運營商,每個地區都有自己的POP點,所以總有更加靠近用戶的相同運營商和相近地點的CDN節點就近獲取靜態數據,避免了跨運營商和跨地域的訪問。

在使用了CDN之后,用戶訪問資源的時候,和上面的過程類似,但是不同的是,DNS解析的時候,會將域名的解析權交給CDN廠商的DNS服務器,而CDN廠商的DNS服務器可以通過CDN廠商的GSLB,找到最最接近客戶的POP點,將數據返回給用戶。

3.jpg


當CDN中沒有找到緩存數據的時候,則需要到真正的服務器中去拿,這個稱為回源,僅僅非常少數的流量需要回源,大大減少了服務器的壓力。

三、數據中心邊界與核心:邊界路由、核心交換、等價路由

如果真的需要回源,或者訪問的壓根就不是靜態資源,而是動態資源,則需要進入數據中心了。

剛才第一節中說到,最終GSLB返回了6個IP地址,都是內部負載均衡SLB的IP地址,說明這6個IP地址都是公網可以訪問的,那么公網如何知道這些IP地址的呢?

這就要看機房的結構了。

4.jpg


一個機房一般會有邊界路由器、核心交換機,每個AZ有匯聚交換機,6個SLB是在AZ里面的,所以他們的IP地址是通過iBGP協議告知邊界路由器的。

當用戶從六個IP里面選擇了一個IP地址進行訪問的時候,可以通過公網上面的路由,找到機房的邊界路由器,邊界路由器知道當時這個路由是從哪個AZ里面給他的,於是就通過核心交換一層,將請求轉發給某一個AZ,這個AZ的匯聚交換機會將請求轉發給這個SLB。

如果一個AZ出現了問題,是否可以讓對某個公網IP的訪問給另一個AZ呢?當然是可以的,在核心路由和核心交換之間,可以做ECMP等價路由。當然也可以在邊界路由上將外部地址NAT稱為內部的一個VIP地址,通過等價路由實現跨AZ的流量分擔。

四、數據中心可用區中:負載均衡SLB、LVS、HAProxy

進入一個可用區AZ之后,首先到達的是負載均衡SLB,可以購買商用的SLB,也可以自己搭建,例如通過LVS實現基本的負載均衡功能。

LVS的性能比較好,很多工作通過內核模塊ipvs完成。

5.png


LVS可使用keepalived實現雙機熱備,也可以通過OSPF使用等價路由的方式,在多個LVS之間進行流量分擔,往往作為統一的負載均衡入口,承載大的流量。

6.jpg


有時候需要更加復雜的4層和7層負載均衡,則會在LVS后面加上HAProxy集群,也即將LVS導入的流量,分發到一大批HAProxy上,這些HAProxy可以根據不同的應用或者租戶進行隔離,每個租戶獨享單獨的HAProxy,但是所有的租戶共享LVS集群。

如果有雲環境,則HAProxy可以部署在虛擬機里面,可以根據流量的情況和租戶的請求進行動態的創建和刪除。

7.png

 

五、數據中心可用區中:接入層Nginx、接入層緩存

在負載均衡之后,是接入網關,或者API網關,往往需要實現很多靈活的轉發策略,這里會選擇使用Nginx+Lua或者OpenResty做這一層。

由於Nginx本身也有負載均衡機制,有的時候會將HAProxy這一層和Nginx這一層合並,LVS后面直接跟Nginx集群。

接入層作用一:API的聚合

使用微服務之后,后端的服務會拆分的非常的細,因而前端應用如果要獲取整個頁面的顯示,往往需要從多個服務獲取數據,將數據做一定的聚合后,方能夠顯示出來。

8.jpg


如果是網頁其實還好,如果你用Chrome的debug模式下,打開一個復雜的電商主頁的時候,你會發現這個頁面同時會發出很多的http的請求,最終聚合稱為一個頁面。

如果是APP的話,其實也沒有問題,但是會有大量的工作要在客戶端做,這樣會非常的耗電,用戶體驗非常不好,因而最好有一個地方可以將請求聚合,這就是API網關的職責之一。這樣對於前端APP來講,后端接是似乎是一個統一的入口,則后端的服務的拆分和聚合,灰度發布,緩存策略等全部被屏蔽了。

9.jpg

 

接入層作用二:服務發現與動態負載均衡

既然統一的入口變為了接入層,則接入層就有責任自動的發現后端拆分、聚合、擴容、縮容的服務集群,當后端服務有所變化的時候,能夠實現健康檢查和動態的負載均衡。

對於微服務來講,服務之間也是需要做服務發現的,常見的框架是Dubbo和Spring Cloud,服務的注冊中心可以是ZooKeeper、Consul、etcd、Eureka等。

我們以Consul為例子,既然服務之間的調用已經注冊到Consul上,則Nginx自然也可以通過Consul來獲取后端服務的狀態,實現動態的負載均衡。

Nginx可以集成consul-template,可監聽Consul的事件, 當已注冊service列表或key/value 發生變化時, consul-template會修改配置文件同時可執行一段shell,如nginx reload。

consul-template \    -template "/tmp/nginx.hcl:/var/nginx/nginx.conf:service nginx reload" \


consul-template模式配置相對復雜,需要reload nginx。

另一種集成Consul的方式是nginx-upsync-module,可以同步Consul的服務列表或key/value存儲,需要重新編譯nginx,不需要reload nginx。

upstream test {

    server 127.0.0.1:11111;

    # 所有的后端服務列表會從consul拉取, 並刪除上面的占位server

    upsync 127.0.0.1:8500/v1/catelog/service/test upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;

    # 備份的地址, 保證nginx不強依賴consul

    upsync_dump_path /usr/local/nginx/conf/servers/servers_test.conf;

} 


還有一種方式是OpenResty+Lua,相對nginx-upsync-module, 可以加入更多自己的邏輯,init_*_by_lua階段通過http api獲取服務列表載入Nginx內存,並設置timer輪訓更新列表,balancer_by_lua階段讀取內存的列表, 設置后端服務器。

Lua實現同樣可以不reload nginx,相比nginx-upsync-module來說更加可擴展。

接入層作用三:動靜資源隔離、靜態頁面緩存、頁面靜態化

為什么靜態資源需要隔離呢,靜態資源往往變化較少,但是卻往往比較大,如果每次都加載,則影響性能,浪費帶寬。其實靜態資源可以預加載,並且可以進行緩存,甚至可以推送到CDN。

所以應該在接入層Nginx中配置動態資源和靜態資源的分離,將靜態資源的url導入到Nginx的本地緩存或者單獨的緩存層如Varnish或者Squid,將動態的資源訪問后端的應用或者動態資源的緩存。

在Nginx中,可以通過配置expires、cache-control、if-modified-since來控制瀏覽器端的緩存控制。使得瀏覽器端在一段時間內,對於靜態資源,不會重復請求服務端。這一層稱為瀏覽器端的緩存。

當有的請求的確到達了接入層Nginx的時候,也不用總是去應用層獲取頁面,可以在接入層Nginx先攔截一部分熱點的請求。在這里可以有兩層緩存。一是Nginx本身的緩存proxy_cache,二是緩存層的Varnish或者Squid。

在使用接入層緩存的時候,需要注意的是緩存key的選擇,不應該包含於用戶相關的信息,如用戶名、地理信息、cookie、deviceid等,這樣相當於每個用戶單獨的一份緩存,使得緩存的命中率比較低。

在分離了靜態和動態資源之后,就存在組合的問題,可以通過Ajax訪問動態資源,在瀏覽器端進行組合,也可以在接入層進行組合。

如果在接入層聚合,或者Varnish進行聚合,則可以讓接入層緩存定時輪詢后端的應用,當有數據修改的時候,進行動態頁面靜態化,這樣用戶訪問的數據到接入層就會被攔截,缺點是更新的速度有些慢,對於大促場景下的並發訪問高的頁面,可以進行如此的處理。

接入層作用四:動態資源緩存

在動靜分離之后,靜態頁面可以很好的緩存,而動態的數據還是會向后端請求,而動態頁面靜態化延時相對比較高,而且頁面數目多的時候,靜態化的工作量也比較大,因而在接入層還可以通過Redis或者Memcached,對動態資源進行緩存。

10.png

 

接入層作用五:資源隔離

接入層的Nginx集群不是一個,而是不同的請求可以有獨立的Nginx集群。

例如搶券或者秒殺系統,會成為熱點中的熱點,因而應該有獨立的nginx集群。

接入層作用六:統一鑒權、認證、過濾

API Gateway的另一個作用是統一的認證和鑒權。

一種是基於session的,當客戶端輸入用戶名密碼之后,API Gateway會向后端服務提交認證和鑒權,成功后生成session,session統一放在Redis里面,則接下來的訪問全部都帶着session進行。

11.png


另一種方式是通過統一的認證鑒權中心,分配token的方式進行。

12.jpg


這是一個三角形的結構,當API Gateway接收到登陸請求的時候,去認證中心請求認證和授權,如果成功則返回token,token是一個加密過的字符串,里面包含很多的認證信息,接下來的訪問中,API Gateway可以驗證這個token是否有效來認證,而真正的服務可以根據token來鑒權。

接入層作用七:限流

在大促過程中,常常會遇到真實的流量遠遠大於系統測試下來的可承載流量,如果這些流量都進來,則整個系統一定垮掉,最后誰也別玩。所以長做的方式是限流。

限流是從上到下貫穿整個應用的,當然接入層作為最外面的屏障,需要做好整個系統的限流。

對於Nginx來講,限流有多種方式,可以進行連接數限制limit_conn,可以進行訪問頻率限制limit_req,可以啟用過載保護sysgurad模塊。

對請求的目標URL進行限流(例如:某個URL每分鍾只允許調用多少次)。

對客戶端的訪問IP進行限流(例如:某個IP每分鍾只允許請求多少次)。

對於被限流的用戶,可以進行相對友好的返回,不同的頁面的策略可以不同。

對於首頁和活動頁,是讀取比較多的,可以返回緩存中的老的頁面,或者APP定時刷新。

對於加入購物車、下單、支付等寫入請求被限流的,可以返回等待頁面,或者返回一個圈圈轉啊轉,如果過了一段時間還轉不出來,就可以返回擠爆了。

對於支付結果返回,如果被限流,需要馬上返回錯誤頁面。

接入層作用八:灰度發布與AB測試

在接入層,由於可以配置訪問路由,以及訪問權重,可以實現灰度發布,或者AB測試,同時上線兩套系統,通過切入部分流量的方式,測試新上系統的穩定性或者是否更受歡迎。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM