美团点评酒店后台故障演练系统
隨著海量請求、節假日峰值流量和與日俱增的系統復雜度出現的,很有可能是各種故障。在分析以往案例時我們發現,如果預案充分,即使出現故障,也能及時應對。它能最大程度降低故障的平均恢復時間(MTTR),進而讓系統可用程度(SLA)維持在相對較高的水平,將故障損失保持在可控范圍內。但是,經過對2016全年酒店后臺研發組所有面向C端系統的線上事故分析后發現,在許多情況下,由于事故處理預案的缺失或者預案本身的不可靠,以及開發人員故障處理經驗的缺失,造成大家在各種報警之中自亂了陣腳,從而貽誤了最佳戰機。
正如上面所講,由“上游流量”和“依賴”導致的故障數量,占了全年故障的45%。
一個經典的case:
2016年3月10日,Tair集群因流量過大掛掉,導致酒店后臺某組一個ID生成器的功能失效,無法獲取ID,插入數據庫失敗。
值班同學找到相應的開發同學,執行之前的預案(切換到基于數據庫的ID生成器),發現不能解決當前問題(有主鍵沖突)。
值班同學經過分析,臨時修改數據庫中的字段值,修復問題。
從上面的例子可以看出,業務方針對系統可能出現的異常情況,雖然一般設有預案,但是缺乏在大流量、有故障情況下的演練,所以往往在故障來臨時,需要用一些臨時手段來彌補預案的不足。
綜上所述,我們要有一套常態化的“故障演練”機制與工具來反復驗證,從而確保我們的服務能在正常情形下表現出正常的行為,在異常狀況下,也要有正確、可控的表現。
這個服務或是工具能執行:
- 容量與性能評估。
- 故障演練,進而進行依賴梳理、預案驗證,保證服務柔性可用。
這樣才能夠做到在節假日與大促時心中有數,在提高系統服務能力的同時增加開發人員應對與處理故障的經驗。
下面,以酒店后臺switch研發組開發的“Faultdrill”系統為例,向大家介紹一下我們在這方面的經驗。
在壓力測試(以下簡稱“壓測”)和故障演練方面,業界已有很多種實踐。
壓測
壓測有單模塊壓測和全鏈路壓測兩種模式。
阿里雙11、京東618和美團外賣都有過線上全鏈路壓測的分享(美團外賣的分享參見美團點評技術沙龍第6期回顧)。
全鏈路壓測有幾點明顯的優勢:
- 基于線上環境,測出的性能數據準確;
- 相較于線下,測試環境完備,不存在單點、低配置等問題;
- 線上環境有完備的監控報警系統。
但與此同時,全鏈路壓測也有較高的實踐成本:
- 需要有明顯的波谷期;
- 需要清理壓測數據,或者申請資源構建影子存儲;
- 真實流量難構造,需要準備虛擬商家和虛擬用戶;
- 需要有完備的監控報警系統。
酒店業務模式和外賣/購物類的業務模式不太一樣。首先,沒有明顯的波峰波谷(夜里也是訂房高峰期,你懂的),因為沒有明顯的波峰波谷,所以清理數據/影子表也會帶來額外的影響。真實流量的構造也是一個老大難問題,需要準備N多的虛擬商家和虛擬用戶。
所以酒店最早推的是單業務模塊級別的壓力測試和故障演練,大家先自掃門前雪。
美團點評內部的通信協議以Thrift為主,業界的相關壓力測試工具也有很多:
- JMeter作為老牌的壓力測試工具,通常作為HTTP協議的測試,也可以通過自定義插件的方式實現Thrift協議的測試。
- TCPCopy的方式主要是關注“真實流量”。
- loading_test是美團點評內部的壓力測試工具。
|工具|使用方式|支持Thrift協議|流量來源|最小粒度| |-|-|-|-|-|-| |loading_test|在代碼中依賴VCR包手動上傳參數日志。需要loading依賴服務方的jar后重新發布|支持|真實copy線上流量|method級別| |JMeter|編寫Thrift插件(針對于接口)|支持|需要自己構造|method級別| |TCPCopy|安裝TCPCopy|支持|真實流量|端口級別,所有流量全copy過來|-|
這幾種方式都不滿足我們的要求,我們的要求是:真實流量、method級別控制、操作簡單。
所以我們準備自己造個輪子 :)
需求:業務方低成本接入,流量在集群級別(AppKey級別,AppKey相當于同樣功能集群的唯一標識,比如訂單搜索集群的AppKey為xx.xx.xx.order.search)以最低成本進行復制、分發,以及最重要的在這個過程中的安全可控等等都是對測試工具、框架的潛在要求。
基于以上,我們開發了流量復制分發服務。它的核心功能是對線上真實流量進行實時復制并按配置分發到指定的機器,來實現像異構數據遷移一樣進行流量定制化的實時復制與遷移。
借助流量復制分發服務進行功能和系統級別的測試,以達到:
- 容量規劃。在穩定與性能保證的基礎上盡可能的節約資源。
- 核心鏈路梳理,強弱依賴區分,并做到服務之間松耦合。
- 系統瓶頸。在真實請求流量加倍下暴露服務瓶頸點。
- 故障獨立,容災降級等等。
故障演練
如果要演練故障,首先要模擬故障(我們不可能真跑去機房把服務器炸了)。自動化的故障模擬系統業界已有實踐,如Netflix的SimianAmy,阿里的MonkeyKing等。
美團點評內部也有類似的工具,casekiller等等。
SimianAmy和casekiller設計思路相仿,都是通過Linux的一些“tc”、“iptables”等工具,模擬制造網絡延時、中斷等故障。這些工具都是需要root權限才可以執行。美團點評的服務器都需要使用非root用戶來啟動進程,所以這種思路暫不可行。
這些工具都有一定要求,比如root權限,比如需要用Hystrix來包裝一下外部依賴。比如我想制造一個表的慢查詢、想制造Redis的某個操作網絡異常,就有些麻煩。
所以我們準備自己造個輪子 :)
需求:業務方低成本接入,流量以最低成本進行故障的“制造”和“恢復”,無需發布、對代碼無侵入就可以在后臺界面上進行故障的場景配置、開啟與停止。
基于以上,我們開發了故障演練系統。它是一個可以針對集群級別(AppKey級別)的所有機器,隨意啟停“故障”的故障演練平臺??梢栽跓o需root權限的前提下,構造任意method級別的延時或者異常類故障。
我們的設計思路是:
流量復制系統
架構設計中參考了DubboCopy的系統設計,增加了一個SDK,解除了對TCPCopy的依賴。
形成以下的流程:
① 需要壓測方先依賴我們的SDK包,在需要壓測的具體實現方法上打上注解@Copy,并注明采樣率simplingRate(默認采樣率為100%)。
@Copy(attribute = CopyMethodAttribute.READ_METHOD, simplingRate = 1.0f) public Result toCopiedMethod() { }② 正式流量來時,異步將流量發往copy-server。 ③ copy-server根據流量中的信息(interface、method、serverAppKey)來獲取壓測配置(影子集群的AppKey,需要放大幾倍)。 ④ 根據壓測配置,對影子集群按照放大倍數開始發包。
協議分析
Thrift原生協議情況下,如果你沒有IDL(或者注解式的定義),你根本無法知道這條消息的長度是多少,自然做不到在沒有IDL的情況下,對報文進行解析轉發。感謝基礎組件同學做的統一協議方面的努力,讓ThriftCopy這個事情有了可行性 :)
除了公網RPC接口使用HTTP協議以外,美團點評內部RPC協議都統一為一種兼容原生Thrift協議的“統一協議”。
total length指定其后消息的總長度,包含2B的header length+消息頭的長度+消息體的長度+可能的4B的校驗碼的長度。header length指定其后消息頭的長度。
header里的內容有TraceInfo和RequestInfo等信息,里面有clientAppKey、interfaceName、methodName等我們需要的信息。
client功能
應用啟動時
RPC請求到來時
以上,便可進行流量的復制與分發,在服務設計上,Client端盡量做到輕量高效,對接入方的影響最小,接入成本低,并且在整個流量復制的過程中安全可控。另外,在Client,當前針對美團點評使用的Thrift協議,進行:
- 流量染色。對原請求在協議層重寫染色其中的clientAppKey和requestMethodName,分別重寫為”“和”${rawMethodName}_copy”在請求接收方可以調用特定方法即可判斷請求是否是由“流量復制分發服務”的轉發請求。
- 讀寫標記。通過在注解上attribute屬性標記轉發接口為讀還是寫接口,為后續的流量分發做好準備。
- 負載均衡。支持服務端的橫向擴展。
- 采樣控制。對流量復制/采樣進行控制,最大限度的定制復制行為。
server功能
應用啟動時
流量到來時
故障演練系統
我們的需求是,可以集群級別(AppKey級別)而不是單機級別輕松的模擬故障。
模擬什么樣的故障呢?
|類型|故障表現| |-|-| |Thrift RPC |延時xxx ms或者直接拋出Exception| |mybatis mapper中的任意method|延時xxx ms或者直接拋出Exception| |Redis/Tair中的任意method|延時xxx ms或者直接拋出exception| |Kafka消息控制|模擬環境可能需要關閉消息生產/消費| |ES|延時xxx ms或者直接拋出exception|
我們調研了很多種實現方式:
|方案|備注| |-|-| |基于各種Linux指令模擬故障(網絡/IO/Load)|無法精細化模擬故障,可操作性差,并且受限于root權限,很多操作無法進行或者無法自動化| |基于HystrixCommand|只能針對接入了Hystrix的接口,且無法精確控制,實現復雜| |基于AOP攔截|對象內部調用無法攔截,非容器對象也無法攔截| |字節碼注入|可以對各種目標進行注入,擴展性高,可以精細化模擬(實現方案有基于classloader替換/Spring LTW/javaagent)|
經過調研對比,選定了基于javaagent進行字節碼注入,來實現對個目標對象的攔截并注入演練邏輯。
client功能
應用啟動時
需要修改啟動時的JVM參數-javaagent:WEB-INF/lib/hotel-switch-faultdrill-agent-1.0.2.jar
方法執行時 1. 執行之前查找當前的策略(map中的對應object),如果沒有就跳過。 2. 如果有就先執行object中的invoke方法。起到“java.lang.Thread.sleep(2000L);”比如“throw new org.apache.thrift.TException(“rpc error”);”等作用。
server的功能
server的功能就比較簡單了,主要是存儲用戶的設置,以及提供給用戶操作故障啟停的界面。
舉個例子
|測試服務|sourceAppKey|targetAppKey|加壓倍數|采樣率| |-|-|-|-|-| |hotel-swtich-api|com.sankuai.hotel.sw.api.beta03|com.sankuai.hotel.sw.api.beta04|5|1.0|
本機單測調用beta03集群上的服務接口distributeGoodsService.queryPrepayList 5000次。
在目標集群beta04上收到此接口的25000次轉發過來的請求。
多次請求,觀察CAT(美團點評開發的開源監控系統,參考之前的博客)報表,其中Receive為接收到的需要轉發的次數,Dispatch為實際轉發數量。
開始模擬故障:Redis故障、MTThrift故障。
請求接口:控貨的filter接口(訪問緩存),故障前、后響應時間對比圖:
客戶端設置超時1s,接收到請求都超。
開啟Thrift接口故障演練,接口:com.meituan.hotel.goods.service.IGoodsService.queryGoodsStrategyModel,延時3s,設置接口超時6s。
故障前后響應時間對比:
這樣就完成了一次加壓情況下的故障演練過程,隨后就可以讓團隊成員按照既定預案,針對故障進行降級、切換等操作,觀察效果。定期演練,縮短操作時間,降低系統不可用時間。
“故障演練系統”目前具備了流量復制和故障演練兩方面的功能。希望能通過這個系統,對酒店后臺的幾個關節模塊進行壓測和演練,提高整體的可用性,為消費者、商家做好服務。
后續“故障演練系統”還會繼續迭代,比如把忙時流量存起來,等閑時再回放;還有如何收集response流量,進而把抽樣的request和response和每天的daily build結合起來;如何在故障演練系統中,模擬更多更復雜的故障等等。會有更多的課題等待我們去攻克,希望感興趣的同學可以一起參與進來,和我們共同把系統做得更好。
分布式會話跟蹤系統架構設計與實踐,美團點評技術博客.
基于TCPCopy的Dubbo服務引流工具-DubboCopy.
從0到1構建美團壓測工具,美團點評技術博客.
javassit.
曾鋆,2013年加入美團點評,就職于美團點評酒旅事業群技術研發部酒店后臺研發組,之前曾在人人網、愛立信、摩托羅拉工作過。
海智,2015年校招加入美團點評,就職于美團點評酒旅事業群技術研發部酒店后臺研發組。
亞輝,2015年加入美團點評,就職于美團點評酒旅事業群技術研發部酒店后臺研發組。
孟瑩,2014年校招加入美團點評,就職于美團點評酒旅事業群技術研發部酒店后臺研發組。
最后發個廣告,美團點評酒旅事業群技術研發部酒店后臺研發組長期招聘Java后臺、架構方面的人才,有興趣的同學可以發送簡歷到xuguanfei@meituan.com。
總結
以上是生活随笔為你收集整理的美团点评酒店后台故障演练系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring boot中使用log4j记
- 下一篇: 前端可用性保障实践