對於互聯網應用和企業大型應用而言,多數都儘量地要求作到7*24小時不間斷運行,而要作到徹底的不間斷運行能夠說「難於上青天」。linux
爲此,對應用的可用性程度通常衡量標準有三個9到五個9。redis
對於一個功能和數據量不斷增長的應用,要保持比較高的可用性並不是易事。爲了實現高可用,付錢拉從避免單點故障、保證應用自身的高可用、解決交易量增加等方面作了許多探索和實踐。數據庫
在不考慮外部依賴系統突發故障,如網絡問題、三方支付和銀行的大面積不可用等狀況下,付錢拉的服務能力可達99.999%。緩存
本文重點討論如何提升應用自身的可用性。安全
爲了提升應用的可用性,首先要作的就是儘量避免應用出現故障,但要徹底作到不出故障是不可能的。互聯網是個容易產生「蝴蝶效應」的地方,任何一個看似很小的、發生機率爲0的事故均可能出現,而後被無限放大。服務器
你們都知道RabbitMQ自己是很是穩定可靠的,付錢拉最開始也一直在使用單點RabbitMQ,而且從未出現運行故障,因此你們在心理上都認爲這個東西不太可能出問題。微信
直到某天,這臺節點所在的物理主機硬件由於年久失修壞掉了,當時這臺RabbitMQ就沒法提供服務,致使系統服務瞬間不可用。網絡
故障發生了也不可怕,最重要的是及時發現並解決故障。付錢拉對自身系統的要求是,秒級發現故障,快速診斷和解決故障,從而下降故障帶來的負面影響。架構
首先簡單回顧一下咱們曾經碰到的一些問題:併發
以史爲鑑
新來的開發同事在處理新接入的三方通道時,因爲經驗不足忽視了設置超時時間的重要性。就是這樣一個小小的細節,致使這個三方隊列所在的交易所有堵塞,同時影響到其餘通道的交易。
系統是分佈式部署的,而且支持灰度發佈,因此環境和部署模塊很是多並且複雜。某次增長了一個新模塊,因爲存在多個環境,且每一個環境都是雙節點,新模塊上線後致使數據庫的鏈接數不夠用,從而影響其餘模塊功能。
一樣是超時問題,一個三方的超時,致使耗盡了當前所配置的全部worker threads,以致於其餘交易沒有可處理的線程。
A三方同時提供鑑權,支付等接口,其中一個接口由於咱們的交易量突增,從而觸發A三方在網絡運營商那邊的DDoS限制。一般機房的出口IP都是固定的,從而被網絡運營商誤認爲是來自這個出口IP的交易是流量攻擊,最終致使A三方鑑權和支付接口同時不可用。
再說一個數據庫的問題,一樣是由於咱們的交易量突增引起的。創建序列的同事給某個序列的上限是999,999,999,但數據庫存的這個字段長度是32位,當交易量小的時候,系統產生的值和字段32位是匹配的,序列不會升位。但是隨着交易量的增長,序列不知不覺的升位數了,結果致使32位就不夠存放。
相似這樣的問題對於互聯網系統很是常見,而且具備隱蔽性,因此如何避免就顯得很是重要了。
下面咱們從三個方面來看咱們所作的改變。
儘量避免故障
>>>>
設計可容錯的系統
好比重路由,對於用戶支付來講,用戶並不關心本身的錢具體是從哪一個通道支付出去的,用戶只關心成功與否。付錢拉鍊接30多個通道,有可能A通道支付不成功,這個時候就須要動態重路由到B或者C通道,這樣就能夠經過系統重路由避免用戶支付失敗,實現支付容錯。
還有針對OOM作容錯,像Tomcat同樣。系統內存總有發生用盡的狀況,若是一開始就對應用自己預留一些內存,當系統發生OOM的時候,就能夠catch住這個異常,從而避免此次OOM。
>>>>
某些環節快速失敗「Fail fast原則」
Fail fast原則是當主流程的任何一步出現問題的時候,應該快速合理地結束整個流程,而不是等到出現負面影響才處理。
舉個幾個例子:
付錢拉啓動的時候須要加載一些隊列信息和配置信息到緩存,若是加載失敗或者隊列配置不正確,會形成請求處理過程的失敗,對此最佳的處理方式是加載數據失敗,JVM直接退出,避免後續啓動不可用;
咱們的實時類交易處理響應時間最長是40s,若是超過40s前置系統就再也不等待,釋放線程,告知商戶正在處理中,後續有處理結果會以通知的方式或者業務線主動查詢的方式獲得結果;
咱們使用了redis作緩存數據庫,用到的地方有實時報警埋點和驗重等功能。若是鏈接redis超過50ms,那麼這筆redis操做會自動放棄,在最壞的狀況下這個操做帶給支付的影響也就是50ms,控制在系統容許的範圍內。
>>>>
設計具有自我保護能力的系統
系統通常都有第三方依賴,好比數據庫、三方接口等。系統開發的時候,須要對第三方保持懷疑,避免第三方出現問題時候的連鎖反應,致使宕機。
(1)拆分消息隊列
咱們提供各類各樣的支付接口給商戶,經常使用的就有快捷,我的網銀,企業網銀,退款,撤銷,批量代付,批量代扣,單筆代付,單筆代扣,語音支付,餘額查詢,身份證鑑權,銀行卡鑑權,卡密鑑權等。與其對應的支付通道有微信支付,ApplePay,支付寶等30多家支付通道,而且接入了幾百家商戶。在這三個維度下,如何確保不一樣業務、三方、商戶、以及支付類型互不影響,咱們所作的就是拆分消息隊列。下圖是部分業務消息隊列拆分圖:
(2)限制資源的使用
對於資源使用的限制設計是高可用系統最重要的一點,也是容易被忽略的一點,資源相對有限,用的過多了,天然會致使應用宕機。爲此咱們作了如下功課:
限制鏈接數
隨着分佈式的橫向擴展,須要考慮數據庫鏈接數,而不是無休止的最大化。數據庫的鏈接數是有限制的,須要全局考量全部的模塊,特別是橫向擴展帶來的增長。
限制內存的使用
內存使用過大,會致使頻繁的GC和OOM,內存的使用主要來自如下兩個方面:
集合容量過大;
未釋放已經再也不引用的對象,好比放入ThreadLocal的對象一直會等到線程退出的時候回收。
限制線程建立
線程的無限制建立,最終致使其不可控,特別是隱藏在代碼中的建立線程方法。
當系統的SY值太高時,表示linux須要花費更多的時間進行線程切換。Java形成這種現象的主要緣由是建立的線程比較多,且這些線程都處於不斷的阻塞(鎖等待,IO等待)和執行狀態的變化過程當中,這就產生了大量的上下文切換。
除此以外,Java應用在建立線程時會操做JVM堆外的物理內存,太多的線程也會使用過多的物理內存。對於線程的建立,最好經過線程池來實現,避免線程過多產生上下文切換。
限制併發
作過支付系統的應該清楚,部分三方支付公司是對商戶的併發有要求的。三方給開放幾個併發是根據實際交易量來評估的,因此若是不控制併發,全部的交易都發給三方,那麼三方只會回覆「請下降提交頻率」。
因此在系統設計階段和代碼review階段都須要特別注意,將併發限制在三方容許的範圍內。
及時發現故障
故障就像鬼子進村,來的猝不及防。當預防的防線被衝破,如何及時拉起第二道防線,發現故障保證可用性,這時候報警監控系統的開始發揮做用了。一輛沒有儀表盤的汽車,是沒法知道車速和油量,轉向燈是否亮,就算「老司機」水平再高也是至關危險的。一樣,系統也是須要監控的,最好是出現危險的時候提早報警,這樣能夠在故障真正引起風險前解決。
>>>>
實時報警系統
若是沒有實時報警,系統運行狀態的不肯定性會形成沒法量化的災難。咱們的監控系統指標以下:
實時性:實現秒級監控;
全面性:覆蓋全部系統業務,確保無死角覆蓋;
實用性:預警分爲多個級別,監控人員能夠方便實用地根據預警嚴重程度作出精確的決策;
多樣性:預警方式提供推拉模式,包括短信,郵件,可視化界面,方便監控人員及時發現問題
報警主要分爲單機報警和集羣報警,而付錢拉屬於集羣部署。實時預警主要依靠各個業務系統實時埋點數據統計分析實現,所以難度主要在數據埋點和分析系統上。
>>>>
埋點數據
要作到實時分析,又不影響交易系統的響應時間,咱們在系統各個模塊中經過redis實時作數據埋點,而後將埋點數據彙總到分析系統,分析系統根據規則進行分析報警。
>>>>
分析系統
分析系統最難作的是業務報警點,例如哪些報警只要一出來就必須出警,哪些報警一出來只須要關注。下面咱們對分析系統作一個詳細介紹:
一、系統運行架構
二、系統運行流程
三、系統業務監控點
咱們的業務監控點都是在平常運行過程當中一點一滴總結出來的,分爲出警類和關注類兩大塊。
出警類:
網絡異常預警;
單筆訂單超時未完成預警;
實時交易成功率預警;
異常狀態預警;
未回盤預警;
失敗通知預警;
異常失敗預警;
響應碼頻發預警;
覈對不一致預警;
特殊狀態預警;
關注類:
交易量異常預警;
交易額超過500W預警;
短信回填超時預警;
非法IP預警;
四、非業務監控點
非業務監控點主要是指從運維角度的監控,包括網絡,主機,存儲,日誌等。具體以下:
服務可用性監控:
使用JVM採集YoungGC/Full GC次數及時間、堆內存、耗時Top 10線程堆棧等信息,包括緩存buffer的長度。
流量監控:
經過Agent監控代理部署在各個服務器上,實時採集流量狀況。
外部系統監控:
經過間隙性探測來觀察三方或者網絡是否穩定。
中間件監控:
針對MQ消費隊列,經過RabbitMQ腳本探測,實時分析隊列深度;
針對數據庫部分,經過安裝插件xdb,實時監控數據庫性能。
實時日誌監控:
經過rsyslog完成分佈式日誌的歸集,而後經過系統分析處理,完成日誌實時監控和分析。最後,經過開發可視化頁面展現給使用者。
系統資源監控:
經過Zabbix監控主機的CPU負載、內存使用率、各網卡的上下行流量、各磁盤讀寫速率、各磁盤讀寫次數(IOPS)、各磁盤空間使用率等。
以上就是咱們實時監控系統所作的,主要分爲業務點監控和運維監控兩方面,雖然系統是分佈式部署,可是每一個預警點都是秒級響應。除此以外,業務系統的報警點也有一個難點,那就是有些報警是少許報出來不必定有問題,大量報警就會有問題,也就是所謂的量變引發質變。
舉一個例子,拿網絡異常來講,發生一筆多是網絡抖動,可是多筆發生就須要重視網絡是否真的有問題,針對網絡異常,咱們的報警樣例以下:
單通道網絡異常預警:1分鐘內A通道網絡異常連續發生了12筆,觸發了預警閥值;
多通道網絡異常預警1: 10分鐘內,連續每分鐘內網絡異常發生了3筆,涉及3個通道,觸發了預警閥值;
多通道網絡異常預警2: 10分鐘內,總共發生網絡異常25筆,涉及3個通道,觸發了預警閥值。
>>>>
日誌記錄和分析系統
對於一個大型系統而言,天天記錄大量的日誌和分析日誌是有必定的難度的。付錢拉天天平均有200W筆訂單量,一筆交易通過十幾個模塊流轉,假設一筆訂單記錄30條日誌,可想而知天天會有多麼巨大的日誌量。
咱們日誌的分析有兩個做用,一個是實時日誌異常預警,另一個是提供訂單軌跡給運營人員使用。
>>>>
實時日誌預警
實時日誌預警是針對全部實時交易日誌,實時抓取帶有Exception或者Error的關鍵字而後報警。這樣的好處是,若是代碼中有任何運行異常,都會第一時間發現。咱們針對實時日誌預警的處理方式是,首先採用rsyslog完成日誌歸集,而後經過分析系統實時抓取,再作實時預警。
>>>>
訂單軌跡
對於交易系統,很是有必要實時瞭解一筆訂單的狀態流轉。咱們最初的作法是經過數據庫來記錄訂單軌跡,可是運行一段時間後,發現訂單量劇增致使數據庫表過大不利於維護。
咱們如今的作法是,每一個模塊經過打印日誌軌跡,日誌軌跡打印的格式按照數據庫表結構的方式打印,打印好全部日誌後,rsyslog來完成日誌歸集,分析系統會實時抓取打印的規範日誌,進行解析而後按天存放到數據庫中,並展現給運營人員可視化界面。
日誌打印規範以下:
簡要日誌可視化軌跡以下:
日誌記錄和分析系統除了以上兩點,也提供了交易和響應報文的下載和查看。
>>>>
7*24小時監控室
以上的報警項目給操做人員提供推拉兩種方式,一種是短信和郵件推送,一種是報表展現。除此以外,因爲支付系統相比互聯網其餘系統自己的重要性,咱們採用7*24小時的監控室保證系統的安全穩定。
及時處理故障
在故障發生以後,特別是生產環境,第一時間要作的不是尋找故障發生的緣由,而是以最快速度處理故障,保障系統的可用性。咱們常見的故障和處理措施以下:
>>>>
自動修復
針對自動修復部分,咱們常見的故障都是三方不穩定形成的,針對這種狀況,就是上面說的系統會自動進行重路由。
>>>>
服務降級
服務降級指在出現故障的狀況下又沒法快速修復的狀況下,把某些功能關閉,以保證核心功能的使用。咱們針對商戶促銷的時候,若是某個商戶交易量過大,會實時的調整這個商戶的流量,使此商戶服務降級,從而不會影響到其餘商戶,相似這樣的場景還有不少,具體的服務降級功能會在後續系列介紹。