天天看點

微服務的接入層設計與動靜資源隔離

本文由  網易雲 釋出。

作者:劉超,網易雲解決方案架構師

這個系列是微服務高并發設計,是以我們先從最外層的接入層入手,看都有什麼樣的政策保證高并發。

接入層的架構如下圖所示:

微服務的接入層設計與動靜資源隔離

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

當我們要通路一個網站的服務的時候,首先通路的肯定是一個域名,然後由DNS,将域名解析為IP位址。

我們首先先通過DNS通路資料中心中的對象存儲上的靜态資源為例子,看一看整個過程。

我們建議将例如檔案,圖檔,視訊,音頻等靜态資源放在對象存儲中,直接通過CDN下發,而非放在伺服器上,和動态資源綁定在一起。

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

微服務的接入層設計與動靜資源隔離

(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廠商的覆寫範圍往往更廣,在每個營運商,每個地區都有自己的POP點,是以總有更加靠近使用者的相同營運商和相近地點的CDN節點就近擷取靜态資料,避免了跨營運商和跨地域的通路。

在使用了CDN之後,使用者通路資源的時候,和上面的過程類似,但是不同的是,DNS解析的時候,會将域名的解析權交給CDN廠商的DNS伺服器,而CDN廠商的DNS伺服器可以通過CDN廠商的GSLB,找到最最接近客戶的POP點,将資料傳回給使用者。

微服務的接入層設計與動靜資源隔離

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

如果真的需要回源,或者通路的壓根就不是靜态資源,而是動态資源,則需要進入資料中心了。

剛才第一節中說到,最終GSLB傳回了6個IP位址,都是内部負載均衡SLB的IP位址,說明這6個IP位址都是公網可以通路的,那麼公網如何知道這些IP位址的呢?

這就要看機房的結構了:

微服務的接入層設計與動靜資源隔離

一個機房一般會有邊界路由器,核心交換機,每個AZ有彙聚交換機,6個SLB是在AZ裡面的,是以他們的IP位址是通過iBGP協定告知邊界路由器的。

當使用者從六個IP裡面選擇了一個IP位址進行通路的時候,可以通過公網上面的路由,找到機房的邊界路由器,邊界路由器知道當時這個路由是從哪個AZ裡面給他的,于是就通過核心交換一層,将請求轉發給某一個AZ,這個AZ的彙聚交換機會将請求轉發給這個SLB。

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

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

LVS的性能比較好,很多工作通過核心子產品ipvs完成。

微服務的接入層設計與動靜資源隔離

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

微服務的接入層設計與動靜資源隔離

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

如果有雲環境,則haproxy可以部署在虛拟機裡面,可以根據流量的情況和租戶的請求進行動态的建立和删除。

微服務的接入層設計與動靜資源隔離

在負載均衡之後,是接入網關,或者API網關,往往需要實作很多靈活的轉發政策,這裡會選擇使用nginx+lua或者openresty做這一層。

由于nginx本身也有負載均衡機制,有的時候會将haproxy這一層和nginx這一層合并,LVS後面直接跟nginx叢集。

接入層作用一:API的聚合。

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

微服務的接入層設計與動靜資源隔離

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

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

微服務的接入層設計與動靜資源隔離

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

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

對于微服務來講,服務之間也是需要做服務發現的,常見的架構是dubbo和springcloud,服務的注冊中心可以是zookeeper, consul, etcd, eureka等。

我們以consul為例子,既然服務之間的調用已經注冊到consul上,則nginx自然也可以通過consul來擷取後端服務的狀态,實作動态的負載均衡。

nginx可以內建consul-template,可監聽consul的事件, 當已注冊service清單或key/value 發生變化時, consul-template會修改配置檔案同時可執行一段shell, 如 nginx reload

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

另一種內建consul的方式是nginx-upsync-module,可以同步consul的服務清單或key/value存儲,需要重新編譯nginx,不需要reload nginx。

還有一種方式是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,對動态資源進行緩存。

微服務的接入層設計與動靜資源隔離

接入層作用五:資源隔離

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

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

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

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

一種是基于session的,當用戶端輸入使用者名密碼之後,API Gateway會向後端服務送出認證和鑒權,成功後生成session,session統一放在redis裡面,則接下來的通路全部都帶着session進行。

微服務的接入層設計與動靜資源隔離

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

微服務的接入層設計與動靜資源隔離

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

接入層作用七:限流

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

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

對于nginx來講,限流有多種方式,可以進行連接配接數限制limit_conn,可以進行通路頻率限制limit_req,可以啟用過載保護sysgurad子產品。

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

對用戶端的通路IP進行限流(例如:某個IP每分鐘隻允許請求多少次)。

對于被限流的使用者,可以進行相對友好的傳回,不同的頁面的政策可以不同。

對于首頁和活動頁,是讀取比較多的,可以傳回緩存中的老的頁面,或者APP定時重新整理。

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

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

接入層作用八:灰階釋出與AB測試

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

了解 網易雲 :

網易雲官網:https://www.163yun.com/

新使用者大禮包:https://www.163yun.com/gift

網易雲社群:https://sq.163yun.com/

繼續閱讀