可用性高達5個9!支付系統高可用架構設計實戰

1、背景

對於互聯網應用和企業大型應用而言,多數都儘量地要求作到7*24小時不間斷運行,而要作到徹底不間斷運行能夠說「難於上青天」。爲此,對應用可用性程度的衡量標準通常有3個9到5個9。php

可用性指標 計算方式 不可用時間(分鐘)
99.9% 0.1%*365*24*60 525.6
99.99% 0.01%*365*24*60 52.56
99.999% 0.001%*365*24*60 5.256

對於一個功能和數據量不斷增長的應用,要保持比較高的可用性並不是易事。爲了實現高可用,宜信支付系統從避免單點故障、保證應用自身的高可用、解決交易量增加等方面作了許多探索和實踐。java

在不考慮外部依賴系統突發故障,如網絡問題、三方支付和銀行的大面積不可用等狀況下,宜信支付系統的服務能力能夠達到99.999%。python

本文重點討論如何提升應用自身的可用性,關於如何避免單點故障和解決交易量增加問題會在其餘系列討論。linux

爲了提升應用的可用性,首先要作的就是儘量避免應用出現故障,但要徹底作到不出故障是不可能的。互聯網是個容易產生「蝴蝶效應」的地方,任何一個看似很小的、發生機率爲0的事故均可能出現,而後被無限放大。redis

你們都知道RabbitMQ自己是很是穩定可靠的,宜信支付系統最開始也一直在使用單點RabbitMQ,而且從未出現運行故障,因此你們在心理上都認爲這個東西不太可能出問題。數據庫

直到某天,這臺節點所在的物理主機硬件由於年久失修壞掉了,當時這臺RabbitMQ就沒法提供服務,致使系統服務瞬間不可用。緩存

故障發生了也不可怕,最重要的是及時發現並解決故障。宜信支付系統對自身系統的要求是,秒級發現故障,快速診斷和解決故障,從而下降故障帶來的負面影響。安全

2、問題

以史爲鑑。首先咱們簡單的回顧一下,宜信支付系統曾經碰到的一些問題:服務器

(1) 新來的開發同事在處理新接入的三方通道時,因爲經驗不足忽視了設置超時時間的重要性。就是這樣一個小小的細節,致使這個三方隊列所在的交易所有堵塞,同時影響到其餘通道的交易;微信

(2) 宜信支付系統是分佈式部署的,而且支持灰度發佈,因此環境和部署模塊很是多並且複雜。某次增長了一個新模塊,因爲存在多個環境,且每一個環境都是雙節點,新模塊上線後致使數據庫的鏈接數不夠用,從而影響其餘模塊功能;

(3) 一樣是超時問題,一個三方的超時,致使耗盡了當前所配置的全部worker threads, 以致於其餘交易沒有可處理的線程;

(4) A三方同時提供鑑權,支付等接口,其中一個接口由於宜信支付系統交易量突增,從而觸發A三方在網絡運營商那邊的DDoS限制。一般機房的出口IP都是固定的,從而被網絡運營商誤認爲是來自這個出口IP的交易是流量攻擊,最終致使A三方鑑權和支付接口同時不可用。

(5) 再說一個數據庫的問題,一樣是由於宜信支付系統交易量突增引起的。創建序列的同事給某個序列的上限是999,999,999,但數據庫存的這個字段長度是32位,當交易量小的時候,系統產生的值和字段32位是匹配的,序列不會升位。但是隨着交易量的增長,序列不知不覺的升位數了,結果致使32位就不夠存放。

相似這樣的問題對於互聯網系統很是常見,而且具備隱蔽性,因此如何避免就顯得很是重要了。

3、解決方案

下面咱們從三個方面來看宜信支付系統所作的改變。

3.1 儘量避免故障

3.1.1 設計可容錯的系統

好比重路由,對於用戶支付來講,用戶並不關心本身的錢具體是從哪一個通道支付出去的,用戶只關心成功與否。宜信支付系統鏈接30多個通道,有可能A通道支付不成功,這個時候就須要動態重路由到B或者C通道,這樣就能夠經過系統重路由避免用戶支付失敗,實現支付容錯。

還有針對OOM作容錯,像Tomcat同樣。系統內存總有發生用盡的狀況,若是一開始就對應用自己預留一些內存,當系統發生OOM的時候,就能夠catch住這個異常,從而避免此次OOM。

3.1.2 某些環節快速失敗「fail fast原則」

Fail fast原則是當主流程的任何一步出現問題的時候,應該快速合理地結束整個流程,而不是等到出現負面影響才處理。

舉個幾個例子:

(1)支付系統啓動的時候須要加載一些隊列信息和配置信息到緩存,若是加載失敗或者隊列配置不正確,會形成請求處理過程的失敗,對此最佳的處理方式是加載數據失敗,JVM直接退出,避免後續啓動不可用;

(2)支付系統的實時類交易處理響應時間最長是40s,若是超過40s前置系統就再也不等待,釋放線程,告知商戶正在處理中,後續有處理結果會以通知的方式或者業務線主動查詢的方式獲得結果;

(3)宜信支付系統使用了redis作緩存數據庫,用到的地方有實時報警埋點和驗重等功能。若是鏈接redis超過50ms,那麼這筆redis操做會自動放棄,在最壞的狀況下這個操做帶給支付的影響也就是50ms,控制在系統容許的範圍內。

3.1.3 設計具有自我保護能力的系統

系統通常都有第三方依賴,好比數據庫,三方接口等。系統開發的時候,須要對第三方保持懷疑,避免第三方出現問題時候的連鎖反應,致使宕機。

(1)拆分消息隊列

宜信支付系統提供各類各樣的支付接口給商戶,經常使用的就有快捷,我的網銀,企業網銀,退款,撤銷,批量代付,批量代扣,單筆代付,單筆代扣,語音支付,餘額查詢,身份證鑑權,銀行卡鑑權,卡密鑑權等。與其對應的支付通道有微信支付,ApplePay,支付寶等30多家支付通道,而且接入了幾百家商戶。在這三個維度下,如何確保不一樣業務、三方、商戶、以及支付類型互不影響,宜信支付系統所作的就是拆分消息隊列。下圖是部分業務消息隊列拆分圖:

(2)限制資源的使用

對於資源使用的限制設計是高可用系統最重要的一點,也是容易被忽略的一點,資源相對有限,用的過多了,天然會致使應用宕機。爲此宜信支付系統作了如下功課:

  • 限制鏈接數

隨着分佈式的橫向擴展,須要考慮數據庫鏈接數,而不是無休止的最大化。數據庫的鏈接數是有限制的,須要全局考量全部的模塊,特別是橫向擴展帶來的增長。

  • 限制內存的使用

內存使用過大,會致使頻繁的GC和OOM,內存的使用主要來自如下兩個方面:

A:集合容量過大;

B:未釋放已經再也不引用的對象,好比放入ThreadLocal的對象一直會等到線程退出的時候回收。

  • 限制線程建立

線程的無限制建立,最終致使其不可控,特別是隱藏在代碼中的建立線程方法。

當系統的SY值太高時,表示linux須要花費更多的時間進行線程切換。Java形成這種現象的主要緣由是建立的線程比較多,且這些線程都處於不斷的阻塞(鎖等待,IO等待)和執行狀態的變化過程當中,這就產生了大量的上下文切換。

除此以外,Java應用在建立線程時會操做JVM堆外的物理內存,太多的線程也會使用過多的物理內存。

對於線程的建立,最好經過線程池來實現,避免線程過多產生上下文切換。

  • 限制併發

作過支付系統的應該清楚,部分三方支付公司是對商戶的併發有要求的。三方給開放幾個併發是根據實際交易量來評估的,因此若是不控制併發,全部的交易都發給三方,那麼三方只會回覆「請下降提交頻率」。

因此在系統設計階段和代碼review階段都須要特別注意,將併發限制在三方容許的範圍內。

咱們講到宜信支付系統爲了實現系統的可用性作了三點改變,其一是儘量避免故障,接下來說後面兩點。

3.2 及時發現故障

故障就像鬼子進村,來的猝不及防。當預防的防線被衝破,如何及時拉起第二道防線,發現故障保證可用性,這時候報警監控系統的開始發揮做用了。一輛沒有儀表盤的汽車,是沒法知道車速和油量,轉向燈是否亮,就算「老司機」水平再高也是至關危險的。一樣,系統也是須要監控的,最好是出現危險的時候提早報警,這樣能夠在故障真正引起風險前解決。

3.2.1 實時報警系統

若是沒有實時報警,系統運行狀態的不肯定性會形成沒法量化的災難。宜信支付系統的監控系統指標以下:

  • 實時性-實現秒級監控;

  • 全面性-覆蓋全部系統業務,確保無死角覆蓋;

  • 實用性-預警分爲多個級別,監控人員能夠方便實用地根據預警嚴重程度作出精確的決策;

  • 多樣性-預警方式提供推拉模式,包括短信,郵件,可視化界面,方便監控人員及時發現問題。

報警主要分爲單機報警和集羣報警,而宜信支付系統屬於集羣部署。實時預警主要依靠各個業務系統實時埋點數據統計分析實現,所以難度主要在數據埋點和分析系統上。

3.2.2 埋點數據

要作到實時分析,又不影響交易系統的響應時間,宜信支付系統在各個模塊中經過redis實時作數據埋點,而後將埋點數據彙總到分析系統,分析系統根據規則進行分析報警。

3.2.3 分析系統

分析系統最難作的是業務報警點,例如哪些報警只要一出來就必須出警,哪些報警一出來只須要關注。下面咱們對分析系統作一個詳細介紹:

(1)系統運行架構

(2)系統運行流程

(3)系統業務監控點

宜信支付系統的業務監控點都是在平常運行過程當中一點一滴總結出來的,分爲出警類和關注類兩大塊。

A:出警類

  • 網絡異常預警;

  • 單筆訂單超時未完成預警;

  • 實時交易成功率預警;

  • 異常狀態預警;

  • 未回盤預警;

  • 失敗通知預警;

  • 異常失敗預警;

  • 響應碼頻發預警;

  • 覈對不一致預警;

  • 特殊狀態預警;

B:關注類

  • 交易量異常預警;

  • 交易額超過500W預警;

  • 短信回填超時預警;

  • 非法IP預警;

3.2.4 非業務監控點

非業務監控點主要是指從運維角度的監控,包括網絡,主機,存儲,日誌等。具體以下:

(1)服務可用性監控

使用JVM採集Young GC/Full GC次數及時間、堆內存、耗時Top 10線程堆棧等信息,包括緩存buffer的長度。

(2)流量監控

經過Agent監控代理部署在各個服務器上,實時採集流量狀況。

(3)外部系統監控

經過間隙性探測來觀察三方或者網絡是否穩定。

(4)中間件監控

  • 針對MQ消費隊列,經過RabbitMQ腳本探測,實時分析隊列深度;

  • 針對數據庫部分,經過安裝插件xdb,實時監控數據庫性能.

(5)實時日誌監控

經過rsyslog完成分佈式日誌的歸集,而後經過系統分析處理,完成日誌實時監控和分析。最後,經過開發可視化頁面展現給使用者。

(6)系統資源監控

經過Zabbix監控主機的CPU負載、內存使用率、各網卡的上下行流量、各磁盤讀寫速率、各磁盤讀寫次數(IOPS)、各磁盤空間使用率等。

以上就是宜信支付系統的實時監控系統所作的,主要分爲業務點監控和運維監控兩方面,雖然系統是分佈式部署,可是每一個預警點都是秒級響應。除此以外,業務系統的報警點也有一個難點,那就是有些報警是少許報出來不必定有問題,大量報警就會有問題,也就是所謂的量變引發質變。

舉一個例子,拿網絡異常來講,發生一筆多是網絡抖動,可是多筆發生就須要重視網絡是否真的有問題,針對網絡異常宜信支付系統的報警樣例以下:

  • 單通道網絡異常預警:1分鐘內A通道網絡異常連續發生了12筆,觸發了預警閥值;

  • 多通道網絡異常預警1: 10分鐘內,連續每分鐘內網絡異常發生了3筆,涉及3個通道,觸發了預警閥值;

  • 多通道網絡異常預警2: 10分鐘內,總共發生網絡異常25筆,涉及3個通道, 觸發了預警閥值.

3.2.5 日誌記錄和分析系統

對於一個大型系統而言,天天記錄大量的日誌和分析日誌是有必定的難度的。宜信支付系統天天平均有200W筆訂單量,一筆交易通過十幾個模塊流轉,假設一筆訂單記錄30條日誌,可想而知天天會有多麼巨大的日誌量。

宜信支付系統日誌的分析有兩個做用,一個是實時日誌異常預警,另一個是提供訂單軌跡給運營人員使用。

(1)實時日誌預警

實時日誌預警是針對全部實時交易日誌,實時抓取帶有Exception或者Error的關鍵字而後報警。這樣的好處是,若是代碼中有任何運行異常,都會第一時間發現。宜信支付系統針對實時日誌預警的處理方式是,首先採用rsyslog完成日誌歸集,而後經過分析系統實時抓取,再作實時預警。

(2)訂單軌跡

對於交易系統,很是有必要實時瞭解一筆訂單的狀態流轉。宜信支付系統最初的作法是經過數據庫來記錄訂單軌跡,可是運行一段時間後,發現訂單量劇增致使數據庫表過大不利於維護。

宜信支付系統如今的作法是,每一個模塊經過打印日誌軌跡,日誌軌跡打印的格式按照數據庫表結構的方式打印,打印好全部日誌後,rsyslog來完成日誌歸集,分析系統會實時抓取打印的規範日誌,進行解析而後按天存放到數據庫中,並展現給運營人員可視化界面。

日誌打印規範以下:

2016-07-22 18:15:00.512||pool-73-thread-4||通道適配器||通道適配器-發三方後||CEX16XXXXXXX5751||16201XXXX337||||||04||9000||【結算平臺消息】處理中||0000105||98XX543210||GHT||03||11||2016-07-22 18:15:00.512||張張||||01||tunnelQuery||true||||Pending||||10.100.140.101||8cff785d-0d01-4ed4-b771-cb0b1faa7f95||10.999.140.101||O001||||0.01||||||||http://10.100.444.59:8080/regression/notice||||240||2016-07-20 19:06:13.000xxxxxxx

||2016-07-22 18:15:00.170||2016-07-22 18:15:00.496xxxxxxxxxxxxxxxxxxxx

||2016-07-2019:06:13.000||||||||01||0103||111xxxxxxxxxxxxxxxxxxxxxxxxx

||8fb64154bbea060afec5cd2bb0c36a752be734f3e9424ba7xxxxxxxxxxxxxxxxxxxx

||622xxxxxxxxxxxxxxxx||9bc195a59dd35a47||f2ba5254f9e22914824881c242d211

||||||||||||||||||||6xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx010||||||||||

簡要日誌可視化軌跡以下:

日誌記錄和分析系統除了以上兩點,也提供了交易和響應報文的下載和查看。

3.2.6 7*24小時監控室

宜信支付系統以上的報警項目給操做人員提供推拉兩種方式,一種是短信和郵件推送,一種是報表展現。除此以外,因爲支付系統相比互聯網其餘系統自己的重要性,宜信支付系統採用7*24小時的監控室保證系統的安全穩定。

3.3 及時處理故障

在故障發生以後,特別是生產環境,第一時間要作的不是尋找故障發生的緣由,而是以最快速度處理故障,保障系統的可用性。宜信支付系統常見的故障和處理措施以下:

3.3.1 自動修復

針對自動修復部分,宜信支付系統常見的故障都是三方不穩定形成的,針對這種狀況,就是上面說的系統會自動進行重路由。

3.3.2 服務降級

服務降級指在出現故障的狀況下又沒法快速修復的狀況下,把某些功能關閉,以保證核心功能的使用。宜信支付系統針對商戶促銷的時候,若是某個商戶交易量過大,會實時的調整這個商戶的流量,使此商戶服務降級,從而不會影響到其餘商戶,相似這樣的場景還有不少,具體的服務降級功能會在後續系列介紹。

4、Q&A

Q1: 能講講當年那臺RabbitMQ宕掉的具體細節和處理方案嗎?

A1: RabbitMQ宕機時間引起了對系統可用性的思考,當時咱們的RabbitMQ自己並無宕機(RabbitMQ仍是很穩定的),宕機的是RabbitMQ所在的硬件機器,可是問題就出在當時RabbiMQ的部署是單點部署,而且你們慣性思惟認爲RabbitMQ不會宕機,從而忽略了它所在的容器,因此這個問題的產生對於咱們的思考就是全部的業務不能夠有單點,包括應用服務器、中間件、網絡設備等。單點不只僅須要從單點自己考慮,好比整個服務作雙份,而後AB測試,固然也有雙機房的。

Q2: 貴公司的開發運維是在一塊兒的嗎?

A2: 咱們開發運維是分開的,今天的分享主要是站在整個系統可用性層面來考慮的,開發偏多,有一部分運維的東西。這些宜信支付系統的走過的路,是我一路見證過的。

Q3: 大家的後臺所有使用的Java嗎?有沒有考慮其餘語言?

A3: 咱們目前系統多數是java,有少數的python、php、C++,這個取決於業務類型,目前java這個階段最適合咱們,可能隨着業務的擴展,會考慮其餘語言。

Q4: 對第三方依賴保持懷疑,可否舉個具體的例子說明下怎麼樣作?萬一第三方徹底不了用了怎麼辦

A4: 系統通常都有第三方依賴,好比數據庫,三方接口等。系統開發的時候,須要對第三方保持懷疑,避免第三方出現問題時候的連鎖反應,致使宕機。你們都知道系統一旦發生問題都是滾雪球的,愈來愈大。好比說咱們掃碼通道,若是隻有一家掃碼通道,當這家掃碼通道發生問題的時候是沒有任何辦法的,因此一開始就對它表示懷疑,經過接入多家通道,若是一旦發生異常,實時監控系統觸發報警後就自動進行路由通道切換,保證服務的可用性;其二,針對不一樣的支付類型、商戶、交易類型作異步消息拆分,確保若是一旦有一種類型的交易發生不可預估的異常後,從而不會影響到其餘通道,這個就比如高速公路多車道同樣,快車和慢車道互不影響。其實整體思路就是容錯+拆分+隔離,這個具體問題具體對待。

Q5: 支付超時後,會出現網絡問題,會不會存在錢已付,訂單丟失,如何作容災及數據一致性,又有沒重放日誌,修過數據?

A5:作支付最重要的就是安全,因此針對訂單狀態咱們都是保守處理策略,所以對於網絡異常的訂單咱們都是設置處理中狀態,而後最終經過主動查詢或者被動接受通知來完成和銀行或者三方的最終一致性。支付系統中,除了訂單狀態還有響應碼問題,你們都知道銀行或者三方都是經過響應碼來響應的,響應碼和訂單狀態的翻譯也是必定要保守策略,確保不會出現資金多付少付等問題。總之這個點的整體思路是,資金安全第一,全部的策略都是白名單原則。

Q6: 剛纔提到過,若某支付通道超時,路由策略會分發至另外一通道,根據那個通道圖可看出,都是不一樣的支付方式,好比支付寶或微信支付,那若是我只想經過微信支付,爲啥不是重試,而要換到另外一通道呢?仍是通道自己意思是請求節點?

A6:首先針對超時不能夠作重路由,由於socket timeout是不能肯定這筆交易是否發送到了三方,是否已經成功或者失敗,若是是成功了,再重試一遍若是成功,針對付款就是多付,這種狀況的資金損失對公司來講不能夠的; 其次,針對路由功能,須要分業務類型,若是是單筆代收付交易,用戶是不關心錢是哪一個通道出去的,是能夠路由的,若是是掃碼通道,用戶若是用微信掃碼,確定最終是走微信,可是咱們有好多中間渠道,微信是經過中間渠道出去的,這裏咱們能夠路由不一樣的中間渠道,這樣最終對於用戶來講仍是微信支付。

Q7: 可否舉例說下自動修復的過程?如何發現不穩定到重路由的細節?

A7: 自動修復也就是經過重路由作容錯處理,這個問題很是好,若是發現不穩定而後去決策重路由。重路由必定是明確當前被重路由的交易沒有成功才能夠路由,不然就會形成多付多收的資金問題。咱們系統目前重路由主要是經過過後和事中兩種方式來決策的,針對過後好比5分鐘以內經過實時預警系統發現某個通道不穩定,那麼就會把當期以後的交易路由到別的通道;針對事中的,主要是經過分析每筆訂單返回的失敗響應碼,響應碼作狀態梳理,明確能夠重發的才作重路由。這裏我指列舉這兩點,其餘的業務點還很是多,鑑於篇幅緣由,不作詳述,可是整體思路是必須有一個內存實時分析系統,秒級決策,這個系統必須快,而後結合實時分析和離線分析作決策支撐,咱們的實時秒級預警系統就作這個事情。

Q8: 商戶促銷有規律嗎?促銷時峯值與平時相比會有多少差異?有技術演練麼?降級的優先級是怎樣的?

A8: 商戶促銷通常咱們會事先常常和商戶保持溝通,事先了解促銷的時間點和促銷量,而後針對性作一些事情;促銷峯值和平時差距很是大,促銷通常都是2個小時以內的比較多,好比有的賣理財產品,促銷也就集中在1個小時以內,因此峯值很是高;技術演練是咱們在瞭解商戶的促銷量,而後預估系統的處理能力,而後提早作演練;降級的優先級主要是針對商戶的,因爲接入咱們的商戶支付場景比較多的,有理財,有代收付,有快捷,有掃碼等等,因此咱們總體原則就是不一樣的商戶之間必定不能夠相互影響,由於不能由於你家作促銷影響了其餘商家。

Q9:rsyslog歸集日誌怎麼存儲的?

A9: 這個是好問題,剛開始咱們的日誌也就是訂單軌跡log是記錄在數據庫表中的,結果發現一筆訂單流轉須要好多模塊,這樣一筆訂單的日誌軌跡就是10筆左右,若是一天400w筆交易的話,這張數據庫表就有問題了,就算拆分也是會影響數據庫性能的,而且這個屬於輔助業務,不該該這樣作。而後,咱們發現寫日誌比寫數據庫好,因此把實時日誌打印成表格的形式,打印到硬盤上,這塊因爲只是實時日誌因此日誌量不大,就是在日誌服務器的一個固定目錄下。因爲日誌都是在分佈式機器上,而後經過歸集日誌到一個集中的地方,這塊是經過掛載存儲的,而後有專門運維團隊寫的程序去實時解析這些表格形式的日誌,最終經過可視化頁面展現到運營操做頁面,這樣運營人員看到的訂單軌跡幾乎是實時的,您關心的怎麼存儲實際上不是啥問題,由於咱們分了實時日誌和離線日誌,而後超過必定時間的離線日誌會切割,最終被刪除。

Q10: 系統監控和性能監控如何配合的?

A10:我理解的系統監控包括了系統性能監控,系統性能監控是系統總體監控的一部分,不存在配合問題,系統性能監控有多個維度,好比應用層面,中間件,容器等。系統的非業務監控能夠查看文章分享。

做者:馮忠旗

來源:宜信技術學院

相關文章
相關標籤/搜索