<b></b>
對于網際網路應用和企業大型應用而言,多數都盡可能地要求做到7*24小時不間斷運作,而要做到完全的不間斷運作可以說“難于上青天”。
為此,對應用的可用性程度一般衡量标準有三個9到五個9。
對于一個功能和資料量不斷增加的應用,要保持比較高的可用性并非易事。為了實作高可用,付錢拉從避免單點故障、保證應用自身的高可用、解決交易量增長等方面做了許多探索和實踐。
在不考慮外部依賴系統突發故障,如網絡問題、三方支付和銀行的大面積不可用等情況下,付錢拉的服務能力可達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實時做資料埋點,然後将埋點資料彙總到分析系統,分析系統根據規則進行分析報警。
分析系統
分析系統最難做的是業務報警點,例如哪些報警隻要一出來就必須出警,哪些報警一出來隻需要關注。下面我們對分析系統做一個詳細介紹:
1、系統運作架構
2、系統運作流程
3、系統業務監控點
我們的業務監控點都是在日常運作過程中一點一滴總結出來的,分為出警類和關注類兩大塊。
出警類: 網絡異常預警; 單筆訂單逾時未完成預警; 實時交易成功率預警; 異常狀态預警; 未回盤預警; 失敗通知預警; 異常失敗預警; 響應碼頻發預警; 核對不一緻預警; 特殊狀态預警; 關注類: 交易量異常預警; 交易額超過500w預警; 短信回填逾時預警; 非法ip預警;
4、非業務監控點
非業務監控點主要是指從運維角度的監控,包括網絡,主機,存儲,日志等。具體如下:
服務可用性監控:
使用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小時的監控室保證系統的安全穩定。
及時處理故障
在故障發生之後,特别是生産環境,第一時間要做的不是尋找故障發生的原因,而是以最快速度處理故障,保障系統的可用性。我們常見的故障和處理措施如下:
自動修複
針對自動修複部分,我們常見的故障都是三方不穩定造成的,針對這種情況,就是上面說的系統會自動進行重路由。
服務降級
服務降級指在出現故障的情況下又無法快速修複的情況下,把某些功能關閉,以保證核心功能的使用。我們針對商戶促銷的時候,如果某個商戶交易量過大,會實時的調整這個商戶的流量,使此商戶服務降級,進而不會影響到其他商戶,類似這樣的場景還有很多,具體的服務降級功能會在後續系列介紹。
<b>本文來自雲栖社群合作夥伴"dbaplus",原文釋出時間:2016-08-08</b>