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

此文已由做者劉超受權網易雲社區發佈。前端

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。nginx

這個系列是微服務高併發設計,因此咱們先從最外層的接入層入手,看都有什麼樣的策略保證高併發。ajax

接入層的架構畫一個簡圖來說包括下面的部分redis

接下來咱們依次解析各個部分以及能夠作的優化。spring

1、數據中心以外:DNS,HttpDNS,GSLBchrome

當咱們要訪問一個網站的服務的時候,首先訪問的確定是一個域名,而後由DNS,將域名解析爲IP地址。shell

咱們首先先經過DNS訪問數據中心中的對象存儲上的靜態資源爲例子,看一看整個過程。後端

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

假設全國有多個數據中心,託管在多個運營商,每一個數據中心三個可用區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服務器徹底由應用的服務商提供,能夠實現徹底自主的全網流量調度。

2、數據中心以外:CDN

對於靜態資源來說,其實在真實的訪問機房內的對象存儲以前,在最最接近用戶的地方,能夠先經過CDN進行緩存,這也是高併發應用的一個整體的思路,能接近客戶,儘可能接近客戶。

CDN廠商的覆蓋範圍每每更廣,在每一個運營商,每一個地區都有本身的POP點,因此總有更加靠近用戶的相同運營商和相近地點的CDN節點就近獲取靜態數據,避免了跨運營商和跨地域的訪問。

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

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

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

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

剛纔第一節中說到,最終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的流量分擔。

4、數據中心可用區中:負載均衡SLB,LVS,Haproxy

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

LVS的性能比較好,不少工做經過內核模塊ipvs完成。

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

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

若是有云環境,則haproxy能夠部署在虛擬機裏面,能夠根據流量的狀況和租戶的請求進行動態的建立和刪除。

5、數據中心可用區中:接入層nginx,接入層緩存

在負載均衡以後,是接入網關,或者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 -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,對動態資源進行緩存。

接入層做用五:資源隔離

接入層的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測試,同時上線兩套系統,經過切入部分流量的方式,測試新上系統的穩定性或者是否更受歡迎。

網易雲計算基礎服務深度整合了 IaaS、PaaS 及容器技術,提供彈性計算、DevOps 工具鏈及微服務基礎設施等服務,幫助企業解決 IT、架構及運維等問題,使企業更聚焦於業務,是新一代的雲計算平臺,點擊可免費試用。

免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊。

文章來源: 網易雲社區

相關文章
相關標籤/搜索