本文是我原創,原文首發於美團點評技術博客,原文地址是:https://mp.weixin.qq.com/s/pxNRzWs3sZmbr-K18FvnrAhtml
背景mysql
每一個系統都有它最核心的指標。好比在收單領域:進件系統第一重要的是保證入件準確,第二重要的是保證上單效率。清結算系統第一重要的是保證準確打款,第二重要的是保證及時打款。咱們負責的系統是美團點評智能支付的核心鏈路,承擔着智能支付100%的流量,內部習慣稱爲核心交易。由於涉及美團點評全部線下交易商家、用戶之間的資金流轉,對於核心交易來講:第一重要的是穩定性,第二重要的仍是穩定性。算法
問題引起sql
做爲一個平臺部門,咱們的理想是第一階段快速支持業務;第二階段把控好一個方向;第三階段觀察好市場的方向,本身去引領一個大方向。數據庫
理想很豐滿,現實是自從2017年初的每日幾十萬訂單,到年末時,單日訂單已經突破700萬,系統面臨着巨大的挑戰。支付通道在增多;鏈路在加長;系統複雜性也相應增長。從最初的POS機到後來的二維碼產品,小白盒、小黑盒、秒付……產品的多元化,系統的定位也在時刻的發生着變化。而系統對於變化的應對速度像是一個在和兔子賽跑的烏龜。編程
因爲業務的快速增加,就算系統沒有任何發版升級,也會忽然出現一些事故。事故出現的頻率愈來愈高,系統自身的升級,也常常是困難重重。基礎設施升級、上下游升級,常常會發生「蝴蝶效應」,毫無徵兆的受到影響。緩存
問題分析安全
核心交易的穩定性問題根本上是怎麼實現高可用的問題。服務器
業界高可用的標準是按照系統宕機時間來衡量的:網絡
由於業界的標準是後驗的指標,考慮到對於平時工做的指導意義,咱們一般採用服務治理平臺OCTO來統計可用性。計算方法是:
業界系統可靠性還有兩個比較經常使用的關鍵指標:
平均無端障時間(Mean Time Between Failures,簡稱MTBF):即系統平均可以正常運行多長時間,才發生一次故障
平均修復時間(Mean Time To Repair,簡稱MTTR):即系統由故障狀態轉爲工做狀態時修理時間的平均值
對於核心交易來講,可用性最好是無端障。在有故障的時候,斷定影響的因素除了時間外,還有範圍。將核心交易的可用性問題分解則爲:
問題解決
1. 發生頻率要低之別人死咱們不死
1.1 消除依賴、弱化依賴和控制依賴
用STAR法則舉一個場景:
咱們要設計一個系統A,完成:使用咱們美團點評的POS機,經過系統A鏈接銀行進行付款,咱們會有一些滿減,使用積分等優惠活動。
分析一下對於系統A的顯性需求和隱性需求:
1>須要接收上游傳過來的參數,參數裏包含商家信息、用戶信息、設備信息、優惠信息。
2>生成單號,將交易的訂單信息落庫。
3>敏感信息要加密。
4>要調用下游銀行的接口。
5>要支持退款。
6>要把訂單信息同步給積分覈銷等部門。
7>要能給商家一個查看訂單的界面。
8>要能給商家進行收款的結算。
基於以上需求,分析一下怎樣才能讓裏面的最核心鏈路「使用POS機付款」穩定。
分析一下:需求1到4是付款必需鏈路,能夠作在一個子系統裏,姑且稱之爲收款子系統。5到8各自獨立,每一個均可以做爲一個子系統來作,具體狀況和開發人員數量、維護成本等有關係。
值得注意的是需求5-8和收款子系統的依賴關係並無功能上的依賴,只有數據上的依賴。即他們都要依賴生成的訂單數據。
收款子系統是整個系統的核心,對穩定性要求很是高。其餘子系統出了問題,收款子系統不能受到影響。
基於上面分析,咱們須要作一個收款子系統和其餘子系統之間的一個解耦,統一管理給其餘系統的數據。這裏稱爲「訂閱轉發子系統」,只要保證這個系統不影響收款子系統的穩定便可。
粗略架構圖以下:
從上圖能夠看到,收款子系統和退款子系統、結算子系統、信息同步子系統、查看訂單子系統之間沒有直接依賴關係。這個架構達到了消除依賴的效果。收款子系統不須要依賴數據訂閱轉發子系統,數據訂閱轉發子系統須要依賴收款子系統的數據。咱們控制依賴,數據訂閱轉發子系統從收款子系統拉取數據,而不須要收款子系統給數據訂閱轉發子系統推送數據。這樣,數據訂閱轉發子系統掛了,收款子系統不受影響。
再說數據訂閱轉發子系統拉取數據的方式。好比數據存在MySQL數據庫中,經過同步Binlog來拉取數據。若是採用消息隊列來進行數據傳輸,對消息隊列的中間件就有依賴關係了。若是咱們設計一個災備方案:消息隊列掛了,直接RPC調用傳輸數據。對於這個消息隊列,就達到了弱化依賴的效果。
1.2 事務中不包含外部調用
外部調用包括對外部系統的調用和基礎組件的調用。外部調用具備返回時間不肯定性的特徵,若是包含在了事務裏必然會形成大事務。數據庫大事務會形成其它對數據庫鏈接的請求獲取不到,從而致使和這個數據庫相關的全部服務處於等待狀態,形成鏈接池被打滿,多個服務直接宕掉。若是這個沒作好,危險指數五顆星。下面的圖顯示出外部調用時間的不可控:
解決方法:
排查各個系統的代碼,檢查在事務中是否存在RPC調用、HTTP調用、消息隊列操做、緩存、循環查詢等耗時的操做,這個操做應該移到事務以外,理想的狀況是事務內只處理數據庫操做。
對大事務添加監控報警。大事務發生時,會收到郵件和短信提醒。針對數據庫事務,通常分爲1s以上、500ms以上、100ms以上三種級別的事務報警。
建議不要用XML配置事務,而採用註解的方式。緣由是XML配置事務,第一可讀性不強,第二切面一般配置的比較氾濫,容易形成事務過大,第三對於嵌套狀況的規則很差處理。
1.3 設置合理的超時和重試
對外部系統和緩存、消息隊列等基礎組件的依賴。假設這些被依賴方忽然發生了問題,咱們系統的響應時間是:內部耗時+依賴方超時時間*重試次數。若是超時時間設置過長、重試過多,系統長時間不返回,可能會致使鏈接池被打滿,系統死掉;若是超時時間設置太短,499錯誤會增多,系統的可用性會下降。
舉個例子:
服務A依賴於兩個服務的數據完成這次操做。平時沒有問題,假如服務B在你不知道的狀況下,響應時間變長,甚至中止服務,而你的客戶端超時時間設置過長,則你完成這次請求的響應時間就會變長,此時若是發生意外,後果會很嚴重。
Java的Servlet容器,不管是Tomcat仍是Jetty都是多線程模型,都用Worker線程來處理請求。這個可配置有上限,當你的請求打滿Worker線程的最大值以後,剩餘請求會被放到等待隊列。等待隊列也有上限,一旦等待隊列都滿了,那這臺Web Server就會拒絕服務,對應到Nginx上返回就是502。若是你的服務是QPS較高的服務,那基本上這種場景下,你的服務也會跟着被拖垮。若是你的上游也沒有合理的設置超時時間,那故障會繼續向上擴散。這種故障逐級放大的過程,就是服務雪崩效應。
解決方法:
首先要調研被依賴服務本身調用下游的超時時間是多少。調用方的超時時間要大於被依賴方調用下游的時間。
統計這個接口99%的響應時間是多少,設置的超時時間在這個基礎上加50%。若是接口依賴第三方,而第三方的波動比較大,也能夠按照95%的響應時間。
重試次數若是系統服務重要性高,則按照默認,通常是重試三次。不然,能夠不重試。
1.4 解決慢查詢
慢查詢會下降應用的響應性能和併發性能。在業務量增長的狀況下形成數據庫所在的服務器CPU利用率急劇攀升,嚴重的會致使數據庫不響應,只能重啓解決。關於慢查詢,能夠參考咱們技術博客以前的文章《MySQL索引原理及慢查詢優化》。
解決方法:
將查詢分紅實時查詢、近實時查詢和離線查詢。實時查詢可穿透數據庫,其它的不走數據庫,能夠用Elasticsearch來實現一個查詢中心,處理近實時查詢和離線查詢。
讀寫分離。寫走主庫,讀走從庫。
索引優化。索引過多會影響數據庫寫性能。索引不夠查詢會慢。DBA建議一個數據表的索引數不超過4個。
不容許出現大表。MySQL數據庫的一張數據表當數據量達到千萬級,效率開始急劇降低。
1.5 熔斷
在依賴的服務不可用時,服務調用方應該經過一些技術手段,向上提供有損服務,保證業務柔性可用。而系統沒有熔斷,若是因爲代碼邏輯問題上線引發故障、網絡問題、調用超時、業務促銷調用量激增、服務容量不足等緣由,服務調用鏈路上有一個下游服務出現故障,就可能致使接入層其它的業務不可用。下圖是對無熔斷影響的魚骨圖分析:
解決方法:
自動熔斷:可使用Netflix的Hystrix或者美團點評本身研發的Rhino來作快速失敗。
手動熔斷:確認下游支付通道抖動或不可用,能夠手動關閉通道。
2. 發生頻率要低之本身不做死
本身不做死要作到兩點:第一本身不做,第二本身不死。
2.1 不做
關於不做,我總結了如下7點:
1>不當小白鼠:只用成熟的技術,不因技術自己的問題影響系統的穩定。
2>職責單一化:不因職責耦合而削弱或抑制它完成最重要職責的能力。
3>流程規範化:下降人爲因素帶來的影響。
4>過程自動化:讓系統更高效、更安全的運營。
5>容量有冗餘:爲了應對競對系統不可用用戶轉而訪問咱們的系統、大促來臨等狀況,和出於容災考慮,至少要保證系統2倍以上的冗餘。
6>持續的重構:持續重構是確保代碼長期沒人動,一動就出問題的有效手段。
7>漏洞及時補:美團點評有安全漏洞運維機制,提醒督促各個部門修復安全漏洞。
2.2 不死
關於不死,地球上有五大不死神獸:能在惡劣環境下中止新陳代謝的「水熊蟲」;能夠返老還童的「燈塔水母」;在硬殼裏休養生息的「蛤蜊」;水、陸、寄生樣樣都成的「渦蟲」;有隱生能力的「輪蟲」。它們的共通特徵用在系統設計領域上就是自身容錯能力強。這裏「容錯」的概念是:使系統具備容忍故障的能力,即在產生故障的狀況下,仍有能力將指定的過程繼續完成。容錯便是Fault Tolerance,確切地說是容故障(Fault),而並不是容錯誤(Error)。
3. 發生頻率要低之不被別人搞死
3.1 限流
在開放式的網絡環境下,對外系統每每會收到不少有意無心的惡意攻擊,如DDoS攻擊、用戶失敗重刷。雖然咱們的隊友各個是精英,但仍是要作好保障,不被上游的疏忽影響,畢竟,誰也沒法保證其餘同窗哪天會寫一個若是下游返回不符合預期就無限次重試的代碼。這些內部和外部的巨量調用,若是不加以保護,每每會擴散到後臺服務,最終可能引發後臺基礎服務宕機。下圖是對無限流影響的問題樹分析:
解決方法:
經過對服務端的業務性能壓測,能夠分析出一個相對合理的最大QPS。
流量控制中用的比較多的三個算法是令牌桶、漏桶、計數器。可使用Guava的RateLimiter來實現。其中SmoothBurstry是基於令牌桶算法的,SmoothWarmingUp是基於漏桶算法的。
核心交易這邊採用美團服務治理平臺OCTO作thrift截流。可支持接口粒度配額、支持單機/集羣配額、支持指定消費者配額、支持測試模式工做、及時的報警通知。其中測試模式是隻報警並不真正節流。關閉測試模式則超過限流閾值系統作異常拋出處理。限流策略能夠隨時關閉。
可使用Netflix的Hystrix或者美團點評本身研發的Rhino來作特殊的針對性限流。
4. 故障範圍要小之隔離
隔離是指將系統或資源分割開,在系統發生故障時能限定傳播範圍和影響範圍。
服務器物理隔離原則
① 內外有別:內部系統與對外開放平臺區分對待。
② 內部隔離:從上游到下游按通道從物理服務器上進行隔離,低流量服務合併。
③ 外部隔離:按渠道隔離,渠道之間互不影響。
線程池資源隔離
Hystrix經過命令模式,將每一個類型的業務請求封裝成對應的命令請求。每一個命令請求對應一個線程池,建立好的線程池是被放入到ConcurrentHashMap中。
注意:儘管線程池提供了線程隔離,客戶端底層代碼也必需要有超時設置,不能無限制的阻塞以至於線程池一直飽和。
信號量資源隔離
開發者可使用Hystrix限制系統對某一個依賴的最高併發數,這個基本上就是一個限流策略。每次調用依賴時都會檢查一下是否到達信號量的限制值,如達到,則拒絕。
5. 故障恢復要快之快速發現
發現分爲事前發現、事中發現和過後發現。事前發現的主要手段是壓測和故障演練;事中發現的主要手段是監控報警;過後發現的主要手段是數據分析。
5.1 全鏈路線上壓測
你的系統是否適合全鏈路線上壓測呢?通常來講,全鏈路壓測適用於如下場景:
① 針對鏈路長、環節多、服務依賴錯綜複雜的系統,全鏈路線上壓測能夠更快更準確的定位問題。
② 有完備的監控報警,出現問題能夠隨時終止操做。
③ 有明顯的業務峯值和低谷。低谷期就算出現問題對用戶影響也比較小。
全鏈路線上壓測的目的主要有:
① 瞭解整個系統的處理能力
② 排查性能瓶頸
③ 驗證限流、降級、熔斷、報警等機制是否符合預期並分析數據反過來調整這些閾值等信息
④ 發佈的版本在業務高峯的時候是否符合預期
⑤ 驗證系統的依賴是否符合預期
全鏈路壓測的簡單實現:
① 採集線上日誌數據來作流量回放,爲了和實際數據進行流量隔離,須要對部分字段進行偏移處理。
② 數據着色處理。能夠用中間件來獲取和傳遞流量標籤。
③ 能夠用影子數據表來隔離流量,可是須要注意磁盤空間,建議若是磁盤剩餘空間不足70%採用其餘的方式隔離流量。
④ 外部調用可能須要Mock。實現上能夠採用一個Mock服務隨機產生和線上外部調用返回時間分佈的時延。
壓測工具上,核心交易這邊使用美團點評開發的pTest。
6. 故障恢復要快之快速定位
定位須要靠譜的數據。所謂靠譜就是和要發現的問題緊密相關的,無關的數據會形成視覺盲點,影響定位。因此對於日誌,要制定一個簡明日誌規範。另外系統監控、業務監控、組件監控、實時分析診斷工具也是定位的有效抓手。
7. 故障恢復要快之快速解決
要解決,提早是發現和定位。解決的速度還取決因而自動化的、半自動化的仍是手工的。核心交易有意向搭建一個高可用系統。咱們的口號是:「不重複造輪子,用好輪子。」這是一個集成平臺,職責是:「聚焦核心交易高可用,更好、更快、更高效。」
美團點評內部可使用的用於發現、定位、處理的系統和平臺很是多,可是若是一個個打開連接或者登錄系統,勢必影響解決速度。因此咱們要作集成,讓問題一站式解決。但願達到的效果舉例以下:
工具介紹
Hystrix
Hystrix實現了斷路器模式來對故障進行監控,當斷路器發現調用接口發生了長時間等待,就使用快速失敗策略,向上返回一個錯誤響應,這樣達到防止阻塞的目的。這裏重點介紹一下Hystrix的線程池資源隔離和信號量資源隔離。
線程池資源隔離
優勢
使用線程能夠徹底隔離第三方代碼,請求線程能夠快速放回。
當一個失敗的依賴再次變成可用時,線程池將清理,並當即恢復可用,而不是一個長時間的恢復。
能夠徹底模擬異步調用,方便異步編程。
缺點
線程池的主要缺點是它增長了CPU,由於每一個命令的執行涉及到排隊(默認使用SynchronousQueue避免排隊),調度和上下文切換。
對使用ThreadLocal等依賴線程狀態的代碼增長複雜性,須要手動傳遞和清理線程狀態(Netflix公司內部認爲線程隔離開銷足夠小,不會形成重大的成本或性能的影響)。
信號量資源隔離
開發者可使用Hystrix限制系統對某一個依賴的最高併發數。這個基本上就是一個限流策略,每次調用依賴時都會檢查一下是否到達信號量的限制值,如達到,則拒絕。
優勢
不新起線程執行命令,減小上下文切換。
缺點
沒法配置斷路,每次都必定會去嘗試獲取信號量。
比較一下線程池資源隔離和信號量資源隔離
線程隔離是和主線程無關的其餘線程來運行的;而信號量隔離是和主線程在同一個線程上作的操做。
信號量隔離也能夠用於限制併發訪問,防止阻塞擴散,與線程隔離的最大不一樣在於執行依賴代碼的線程依然是請求線程。
線程池隔離適用於第三方應用或者接口、併發量大的隔離;信號量隔離適用於內部應用或者中間件;併發需求不是很大的場景。
Rhino
Rhino是美團點評基礎架構團隊研發並維護的一個穩定性保障組件,提供故障模擬、降級演練、服務熔斷、服務限流等功能。和Hystrix對比:
內部經過CAT(美團點評開源的監控系統,參見以前的博客「深度剖析開源分佈式監控CAT」)進行了一系列埋點,方便進行服務異常報警。
接入配置中心,能提供動態參數修改,好比強制熔斷、修改失敗率等。
總結思考
王國維 在《人間詞話》裏談到了治學經驗,他說:古今之成大事業、大學問者,必通過三種之境界:
第一種境界
昨夜西風凋碧樹。獨上高樓,望盡天涯路。
第二種境界
衣帶漸寬終不悔,爲伊消得人憔悴。
第三種境界
衆裏尋他千百度,驀然回首,那人卻在,燈火闌珊處。
核心交易的高可用目前正在經歷第一種:高瞻遠矚認清前人所走的路,以總結和學習前人的經驗作爲起點。
下一階段,既然認定了目標,咱們會嘔心瀝血孜孜以求,持續發展高可用。最終,當咱們作了不少的事情,回過頭來看,相信會對高可用有更清晰和深刻的認識。敬請期待咱們下一次的分享~~
關於做者
曉靜,20歲時畢業於東北大學計算機系。在畢業後的第一家公司因爲出衆的語言天賦,在1年的時間裏從零開始學日語並以超高分經過了國際日語一級考試,擔當兩年日語翻譯的工做。後就任於人人網,轉型作互聯網開發。中國科學院心理學研究生。有近百個技術發明專利,創業公司合夥人。有日本東京,美國硅谷技術支持經驗。目前任美團點評技術專家,負責核心交易。(歡迎關注靜兒的我的技術公衆號:編程一輩子 )
---------- END ----------
招聘信息
美團金融核心交易招聘實習生,要求:19年即將畢業的研究生,Java方向,有技術追求。高速發展的業務須要高速發展的團隊,做爲核心部門,咱們急需相信技術改變世界的你!有意者請關注個人我的技術公衆號並留言。