經驗分享:如何構建億級前端讀服務


視頻彙總首頁:http://edu.51cto.com/lecturer/index/user_id-4626073.htmlhtml

======================================前端


一些設計原則nginx


  • 無狀態redis

  • 數據閉環後端

  • 緩存銀彈瀏覽器

  • 併發化緩存

  • 降級開關tomcat

  • 限流服務器

  • 切流量微信

  • 其餘


無狀態


若是設計的應用是無狀態的,那麼應用就能夠水平擴展,固然實際生產環境多是這樣子的: 應用無狀態,配置文件有狀態。好比不一樣的機房須要讀取不一樣的數據源,此時就須要經過配置文件指定。


數據閉環


若是依賴的數據來源特別多,此時就能夠考慮使用數據閉環,基本步驟:


一、數據異構:經過如MQ機制接收數據變動,而後原子化存儲到合適的存儲引擎,如redis或持久化KV存儲;


二、數據聚合:這步是可選的,數據異構的目的是把數據從多個數據源拿過來,數據聚合目的是把這些數據作個聚合,這樣前端就能夠一個調用拿到全部數據,此步驟通常存儲到KV存儲中;


三、前端展現:前端經過一次或少許幾回調用拿到所須要的數據。


這種方式的好處就是數據的閉環,任何依賴系統出問題了,仍是能正常工做,只是更新會有積壓,可是不影響前端展現。


另外此處若是一次須要多個數據,能夠考慮使用Hash Tag機制將相關的數據聚合到一個實例,如在展現商品詳情頁時須要:商品基本信息:p:123:, 商品規格參數:d:123:,此時就可使用冒號中間的123做爲數據分片key,這樣相同id的商品相關數據就在一個實例。


緩存銀彈


緩存對於讀服務來講可謂抗流量的銀彈。


瀏覽器端緩存


設置請求的過時時間,如響應頭Expires、Cache-control進行控制。這種機制適用於如對實時性不太敏感的數據,如商品詳情頁框架、商家評分、評價、廣告詞等;但對於如價格、庫存等實時要求比較高的,就不能作瀏覽器端緩存。


CDN緩存


有些頁面/活動頁/圖片等服務能夠考慮將頁面/活動頁/圖片推送到離用戶最近的CDN節點讓用戶能在離他最近的節點找到想要的數據。通常有兩種機制:推送機制(當內容變動後主動推送到CDN邊緣節點),拉取機制(先訪問邊緣節點,當沒有內容時回源到源服務器拿到內容並存儲到節點上),兩種方式各有利弊。 使用CDN時要考慮URL的設計,好比URL中不能有隨機數,不然每次都穿透CDN,回源到源服務器,至關於CDN沒有任何效果。對於爬蟲能夠返回過時數據而選擇不回源。


接入層緩存


對於沒有CDN緩存的應用來講,能夠考慮使用如Nginx搭建一層接入層,該接入層能夠考慮以下機制實現:


一、URL重寫:將URL按照指定的順序或者格式重寫,去除隨機數;


二、一致性哈希:按照指定的參數(如分類/商品編號)作一致性Hash,從而保證相同數據落到一臺服務器上;


三、proxy_cache:使用內存級/SSD級代理緩存來緩存內容;


四、proxy_cache_lock:使用lock機制,將多個回源合併爲一個,減小回源量,並設置相應的lock超時時間;


五、shared_dict:此處若是架構使用了nginx+lua實現,能夠考慮使用lua shared_dict進行cache,最大的好處就是reload緩存不丟失。


此處要注意,對於託底/異常數據不該該讓其緩存,不然用戶會在很長一段時間看到這些數據。


應用層緩存


如咱們使用Tomcat時可使用堆內緩存/堆外緩存,堆內緩存的最大問題就是重啓時內存中的緩存丟失,若是此時流量風暴來臨可能沖垮應用;還能夠考慮使用local redis cache來代替堆外內存;或者在接入層使用shared_dict來將緩存前置,減小風暴。


分佈式緩存


一種機制就是廢棄分佈式緩存,改爲應用local redis cache,即在應用所在服務器中部署一個redis,而後使用主從機制同步數據。若是數據量不大這種架構是最優的;若是數據量太大,單服務器存儲不了,還能夠考慮分片機制將流量分散到多臺;或者直接就是分佈式緩存實現。常見的分片規則就是一致性哈希了。


wKioL1XCuzHBR15DAADYkI-vIpo250.jpg


如上圖就是咱們一個應用的架構:


一、首先接入層讀取本地proxy cache / local cache;


二、若是不命中,會讀取分佈式redis集羣;


三、若是還不命中,會回源到tomcat,而後讀取堆內cache;若是沒有,則直接調用依賴業務獲取數據;而後異步化寫到redis集羣;


由於咱們使用了nginx+lua,第2、三步可使用lua-resty-lock非阻塞鎖減小峯值時的回源量;若是你的服務是用戶維度的,這種非阻塞鎖不會有什麼大做用。


併發化


假設一個讀服務是須要以下數據:


一、數據A 10ms


二、數據B 15ms


三、數據C 20ms


四、數據D 5ms


五、數據E 10ms


那麼若是串行獲取那麼須要:60ms;


而若是數據C依賴數據A和數據B、數據D誰也不依賴、數據E依賴數據C;那麼咱們能夠這樣子來獲取數據:


wKiom1XCuUORKewHAABPv2gqkhw450.jpg

那麼若是併發化獲取那麼須要:30ms;能提高一倍的性能。


假設數據E還依賴數據F(5ms),而數據F是在數據E服務中獲取的,此時就能夠考慮在此服務中在取數據A/B/D時預取數據F,那麼總體性能就變爲了:25ms。


降級開關


對於一個讀服務,很重要的一個設計就是降級開關,在設計降級開關時主要以下思路:


一、開關集中化管理:經過推送機制把開關推送到各個應用;


二、可降級的多級讀服務:好比只讀本地緩存、只讀分佈式緩存、或者只讀一個默認的降級數據;


三、開關前置化:如架構是nginx—>tomcat,能夠將開關前置到nginx接入層,在nginx層作開關,請求不打到後端應用。


限流


目的是防止惡意流量,惡意***,能夠考慮以下思路:


一、惡意流量只訪問cache;


二、對於穿透到後端應用的能夠考慮使用nginx的limit模塊處理;


三、對於惡意ip可使用如nginx deny進行屏蔽。


大部分時候是不進行接入層限流的,而是限制流量穿透到後端薄弱的應用層。


切流量


對於一個大型應用,切流量是很是重要的,好比多機房有機房掛了、或者有機架掛了、或者有服務器掛了等都須要切流量,可使用以下手段進行切換:


一、DNS:切換機房入口;


二、LVS/HaProxy:切換故障的nginx接入層;


三、Nginx:切換故障的應用層;


另外咱們有些應用爲了更方便切換,還能夠在nginx接入層作切換,經過nginx進行一些流量切換,而沒有經過如LVS/HaProxy作切換。


其餘


不須要cookie的應用使用無狀態域名,如3.cn;


接入層請求頭過濾,只轉發有用的請求頭到後端應用;


數據過濾邏輯前置,好比在接入層進行請求參數的合法性過濾;


內網設置合理的鏈接、讀、寫超時時間;


根據須要開啓gzip壓縮減小流量;


使用unix domain socket減小本機鏈接數;


內網考慮使用http長鏈接;


響應請求時,考慮響應頭加上服務器ip等信息,方便調試。


咱們處理的讀服務大部分都是KV的,所以抗流量的思路就是大量緩存;並且怎麼讓緩存怎麼更接近用戶,離用戶越近速度就越快。再一個點就是要考慮好降級方案,在異常狀況下應用不被拖垮拖死。咱們系統大量使用瞭如nginx+lua+redis技術,使用這些技術解決了咱們不少讀服務問題。


 微信訂閱號

wKiom1XCvSHCrSJRAAC1mdY8jWo590.jpg

相關文章
相關標籤/搜索