實踐解析:大眾點評賬號業務高可用進階之路

趙鈺瑩發表於2018-06-05

  在任何一家網際網路公司,不管其主營業務是什麼,都會有一套自己的賬號體系。賬號既是公司所有業務發展留下的最寶貴資產,它可以用來衡量業務指標,例如日活、月活、留存等,同時也給不同業務線提供了大量潛在使用者,業務可以基於賬號來做使用者畫像,制定各自的發展路徑。因此,賬號服務的重要性不言而喻,同時美團業務飛速發展,對賬號業務的可用性要求也越來越高。本文將分享一些我們在高可用探索中的實踐。

  衡量一個系統的可用性有兩個指標:

  1. MTBF (Mean Time Between Failure)即平均多長時間不出故障;

  2. MTTR (Mean Time To Recovery)即出故障後的平均恢復時間。

  通過這兩個指標可以計算出可用性,也就是我們大家比較熟悉的“幾個9”。

實踐解析:大眾點評賬號業務高可用進階之路

  因此提升系統的可用性,就得從這兩個指標入手,要麼降低故障恢復的時間,要麼延長不出故障的時間。

  1. 業務監控

  要降低故障恢復的時間,首先得儘早的發現故障,然後才能解決故障,這就需要依賴業務監控系統。

  業務監控不同於其他監控系統,業務監控關注的是各個業務指標是否正常,比如賬號的登入曲線。大眾點評登入入口有很多,從終端上分有App、PC、M站,從登入型別上分有密碼登入、快捷登入、第三方登入(微信/QQ/微博)、小程式登入等。需要監控的維度有登入總數、成功數、失敗分類、使用者地區、App版本號、瀏覽器型別、登入來源Referer、服務所在機房等等。業務監控最能從直觀上告訴我們系統的執行狀況。

  由於業務監控的維度很多很雜,有時還要增加新的監控維度,並且告警分析需要頻繁聚合不同維度的資料,因此我們採用Elasticsearch作為日誌儲存。整體架構如下圖:

實踐解析:大眾點評賬號業務高可用進階之路

  每條監控都會根據過去的業務曲線計算出一條基線(見下圖),用來跟當前資料做對比,超出設定的閾值後就會觸發告警。

實踐解析:大眾點評賬號業務高可用進階之路

  每次收到告警,我們都要去找出背後的原因,如果是流量漲了,是有活動了還是被刷了?如果流量跌了,是日誌延時了還是服務出問題了?另外值得重視的是告警的頻次,如果告警太多就會稀釋大家的警惕性。我們曾經踩過一次坑,因為告警太多就把告警關了,結果就在關告警的這段時間業務出問題了,我們沒有及時發現。為了提高每條告警的定位速度,我們在每條告警後面加上維度分析。如下圖(非真實資料),告警裡直接給出分析結果。

實踐解析:大眾點評賬號業務高可用進階之路

  2. 柔性可用

  柔性可用的目的是延長不出故障的時間,當業務依賴的下游服務出故障時不影響自身的核心功能或服務。賬號對上層業務提供的鑑權和查詢服務即核心服務,這些服務的QPS非常高,業務方對服務的可用性要求很高,別說是服務故障,就連任何一點抖動都是不能接受的。對此我們先從整體架構上把服務拆分,其次在服務內對下游依賴做資源隔離,都儘可能的縮小故障發生時的影響範圍。

  另外對非關鍵路徑上的服務故障做了降級。例如賬號的一個查詢服務依賴Redis,當Redis抖動的時候服務的可用性也隨之降低,我們通過公司內部另外一套快取中介軟體Tair來做Redis的備用儲存,當檢測到Redis已經非常不可用時就切到Tair上。

  通過開源元件Hystrix或者我們公司自研的中介軟體Rhino就能非常方便地解決這類問題,其原理是根據最近一個時間視窗內的失敗率來預測下一個請求需不需要快速失敗,從而自動降級,這些步驟都能在毫秒級完成,相比人工干預的情況提升幾個數量級,因此係統的可用性也會大幅提高。下圖是優化前後的對比圖,可以非常明顯的看到,系統的容錯能力提升了,TP999也能控制在合理範圍內。

實踐解析:大眾點評賬號業務高可用進階之路

實踐解析:大眾點評賬號業務高可用進階之路

  對於關鍵路徑上的服務故障我們可以減少其影響的使用者數。比如手機快捷登入流程裡的某個關鍵服務掛了,我們可以在返回的失敗文案上做優化,並且在登入入口掛小黃條提示,讓使用者主動去其他登入途徑,這樣對於那些設定過密碼或者繫結了第三方的使用者還有其他選擇。

  具體的做法是我們在每個登入入口都關聯了一個計數器,一旦其中的關鍵節點不可用,就會在受影響的計數器上加1,如果節點恢復,則會減1,每個計數器還分別對應一個標誌位,當計數器大於0時,標誌位為1,否則標誌位為0。我們可以根據當前標誌位的值得知登入入口的可用情況,從而在登入頁展示不同的提示文案,這些提示文案一共有2^5=32種。

實踐解析:大眾點評賬號業務高可用進階之路

  下圖是我們在做故障模擬時的降級提示文案:

實踐解析:大眾點評賬號業務高可用進階之路

  3. 異地多活

  除了柔性可用,還有一種思路可以來延長不出故障的時間,那就是做冗餘,冗餘的越多,系統的故障率就越低,並且是呈指數級降低。不管是機房故障,還是儲存故障,甚至是網路故障,都能依賴冗餘去解決,比如資料庫可以通過增加從庫的方式做冗餘,服務層可以通過分散式架構做冗餘,但是冗餘也會帶來新的問題,比如成本翻倍,複雜性增加,這就要衡量投入產出比。

  目前美團的資料中心機房主要在北京上海,各個業務都直接或間接的依賴賬號服務,儘管公司內已有北上專線,但因為專線故障或抖動引發的賬號服務不可用,間接導致的業務損失也不容忽視,我們就開始考慮做跨城的異地冗餘,即異地多活。

  3.1 方案設計

  首先我們調研了業界比較成熟的做法,主流思路是分set化,優點是非常利於擴充套件,缺點是隻能按一個維度劃分。比如按使用者ID取模劃分set,其他的像手機號和郵箱的維度就要做出妥協,尤其是這些維度還有唯一性要求,這就使得資料同步或者修改都增加了複雜度,而且極易出錯,給後續維護帶來困難。考慮到賬號讀多寫少的特性(讀寫比是350:1),我們採用了一主多從的資料庫部署方案,優先解決讀多活的問題。

  Redis如果也用一主多從的模式可行嗎?答案是不行,因為Redis主從同步機制會優先嚐試增量同步,當增量同步不成功時,再去嘗試全量同步,一旦專線發生抖動就會把主庫拖垮,並進一步阻塞專線,形成“雪崩效應”。因此兩地的Redis只能是雙主模式,但是這種架構有一個問題,就是我們得自己去解決資料同步的問題,除了保證資料不丟,還要保證資料一致。

  另外從使用者進來的每一層路由都要是就近的,因此DNS需要開啟智慧解析,SLB要開啟同城策略,RPC已預設就近訪問。

  總體上賬號的異地多活遵循以下三個原則:

  1. 北上任何一地故障,另一地都可提供完整服務。2. 北上兩地同時對外提供服務,確保服務隨時可用。3. 兩地服務都遵循BASE原則,確保資料最終一致。

  最終設計方案如下:

實踐解析:大眾點評賬號業務高可用進階之路

  3.2 資料同步

  首先要保證資料在傳輸的過程中不能丟,因此需要一個可靠接收資料的地方,於是我們採用了公司內部的MQ平臺Mafka(類Kafka)做資料中轉站。可是訊息在經過Mafka傳遞之後可能是亂序的,這導致對同一個key的一串操作序列可能導致不一致的結果,這是不可忍受的。但Mafka只是不保證全域性有序,在單個partition內卻是有序的,於是我們只要對每個key做一遍一致性雜湊演算法對應一個partitionId,這樣就能保證每個key的操作是有序的。

  但僅僅有序還不夠,兩地的併發寫仍然會造成資料的不一致。這裡涉及到分散式資料的一致性問題,業界有兩種普遍的認知,一種是Paxos協議,一種是Raft協議。我們吸取了對實現更為友好的Raft協議,它主張有一個主節點,其餘是從節點,並且在主節點不可用時,從節點可晉升為主節點。簡單來說就是把這些節點排個序,當寫入有衝突時,以排在最前面的那個節點為準,其餘節點都去follow那個主節點的值。在技術實現上,我們設計出一個版本號(見下圖),實際上是一個long型整數,其中資料來源大小即表示節點的順序,把版本號存入value裡面,當兩個寫入發生衝突的時候只要比較這個版本號的大小即可,版本號大的覆蓋小的,這樣能保證寫衝突時的資料一致性。

實踐解析:大眾點評賬號業務高可用進階之路

  寫併發時資料同步過程如下圖:

實踐解析:大眾點評賬號業務高可用進階之路

  這種同步方式的好處顯而易見,可以適用於所有的Redis操作且能保證資料的最終一致性。但這也有一些弊端,由於多存了版本號導致Redis儲存會增加,另外在該機制下兩地的資料其實是全量同步的,這對於那些僅用做快取的儲存來說是非常浪費資源的,因為快取有資料庫可以回源。而賬號服務幾乎一半的Redis儲存都是快取,因此我們需要對快取同步做優化。

  賬號服務的快取載入與更新模式如下圖:

實踐解析:大眾點評賬號業務高可用進階之路

  我們優化的方向是在快取載入時不同步,只有在資料庫有更新時才去同步。但是資料更新這個流程裡不能再使用delete操作,這樣做有可能使快取出現髒資料,比如下面這個例子:

實踐解析:大眾點評賬號業務高可用進階之路

  我們對這個問題的解決辦法是用set(若key不存在則新增,否則覆蓋)代替delete,而快取的載入用add(若key不存在則新增,否則不修改),這樣能保證快取更新時的強一致性卻不需要增加額外儲存。考慮到賬號修改的入口比較多,我們希望快取更新的邏輯能拎出來單獨處理減少耦合,最後發現公司內部資料同步元件Databus非常適用於該場景,其主要功能是把資料庫的變更日誌以訊息的形式發出來。於是優化後的快取模式如下圖:

實踐解析:大眾點評賬號業務高可用進階之路

  從理論變為工程實現的時候還有些需要注意的地方,比如同步訊息沒發出去、資料收到後寫失敗了。因此我們還需要一個方法來檢測資料不一致的數量,為了做到這點,我們新建了一個定時任務去scan兩地的資料做對比統計,如果發現有不一致的還能及時修復掉。

  專案上線後,我們也取得一些成果,首先效能提升非常明顯,異地的呼叫平均耗時和TP99、TP999均至少下降80%,並且在一次線上專線故障期間,賬號讀服務對外的可用性並沒有受影響,避免了更大範圍的損失。

  總結

  服務的高可用需要持續性的投入與維護,比如我們會每月做一次容災演練。高可用也不止體現在某一兩個重點專案上,更多的體現在每個業務開發同學的日常工作裡。任何一個小Bug都可能引起一次大的故障,讓你前期所有的努力都付之東流,因此我們的每一行程式碼,每一個方案,每一次線上改動都應該是仔細推敲過的。高可用應該成為一種思維方式。最後希望我們能在服務高可用的道路上越走越遠。

  本文轉載自美團技術團隊微信公眾號,作者: 堂堂 德鑫 楊正,轉載授權請聯系原創作者(meituantech)!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31077337/viewspace-2155641/,如需轉載,請註明出處,否則將追究法律責任。

相關文章