摘要:爲了實現阿里九遊遊戲接入系統的業務高可用,技術人員跳出傳統的面向系統的高可用的思路,轉而從業務的角度來總體考慮高可用,最終實現了一套立體化的高可用架構,本文逐一展現這套立體化高可用架構的一些具體實踐。redis
一般狀況下咱們在談論高可用架構設計的時候,主要關注的是系統結構的高可用,例如主備架構、集羣架構、多中心架構。咱們作架構設計的時候,也主要是從系統結構自己出發,例如咱們把單機改成雙機、雙機改成集羣、單機房改成異地多機房等等。算法
這種以系統結構爲目標的高可用架構設計,更多的是從技術角度出發,但其實咱們都知道,不管技術多先進,架構多強大,都不可能保證100%不出問題的。藍翔的挖掘機一鏟子下去,支付寶就要故障2小時;程序員的誤操做,攜程12小時不能提供業務;某個黑客1000臺肉雞攻過來,網站可能就拒絕服務了......數據庫
所以,真正作到高可用,不能單純從系統結構的角度出發來考慮問題,不能侷限於某個系統的高可用,而應該從業務的角度出發,全方位的來考慮高可用的架構設計。前者我稱之爲「面向系統的高可用架構」,後者我稱之爲「面向業務的高可用架構」。編程
阿里九遊遊戲接入系統(如下簡稱「遊戲接入系統」)負責全部九遊平臺遊戲的接入(包括用戶的登陸、註冊、支付等業務,也包括遊戲開發商(如下簡稱「CP」)用戶驗證、支付等業務),對可用性的要求很是高,一旦故障,大量用戶就不能愉快的玩遊戲,投訴就會滿天飛,論壇就被報障的帖子刷爆了,由於對於不少用戶來講,不上微信可能問題不大,但要是幾小時不能玩遊戲,那就要爆粗口了。如何保證遊戲接入系統的高可用,讓用戶可以愉快的玩遊戲,成爲了咱們的一個巨大的挑戰。緩存
爲了實現遊戲接入業務的高可用,咱們跳出傳統的面向系統的高可用的思路,轉而從業務的角度來總體考慮高可用,最終實現了一套「立體化的高可用架構」。接下來我將逐一展現這套立體化高可用架構的一些具體實踐。服務器
業界高可用的通用指標是幾個9,例如5個9表明一年業務不可用的時間是5分鐘,4個9表明一年業務的不可用時間是50分鐘。咱們最初也是使用這個指標來做爲咱們高可用的目標,可是在實際的操做和討論過程當中,發現這幾個指標雖然簡單,可是並不能直觀的理解,並且對於咱們分析問題和設計方案沒有很強的指導意義,所以咱們決定找更加容易理解和操做的目標。微信
但提及來容易作起來難,高可用自己就是一個主觀色彩比較強的概念,不一樣的人理解都不一致,要肯定一個更加容易理解和操做、你們又能達成一致意見的目標,居然成了咱們首要面對的難題。咱們前後討論了屢次,先後使用了「提升可用性」、「具有XX能力」、「解決存在的可用性問題」……等多個目標,但這些目標最後都被否決了,主要緣由就是咱們的BOSS認爲這些都 無法量化,無法評估,不能認爲作了事情就必定可以達到目標。當時研發團隊和BOSS還有一點小分歧:研發團隊認爲除了幾個9外,高可用無法量化;而BOSS認爲一切均可以量化,只是咱們還沒找到方法。不得已,團隊又繼續頭腦風暴,功夫不負有心人,終於在一次討論中想出了一個可量化可衡量的高可用目標: 3分鐘定位問題、5分鐘恢復業務、平均最多2個月發生一次問題 ,這樣計算下來一年不可用的時間大約就是50分鐘,正好契合4個9的業界通用的可用性目標。網絡
在後來的項目執行過程當中,咱們發現這個目標真的是很是有用,很是具備指導意義,具體表現爲:session
後來的項目總結時,咱們都認爲這個目標是項目成功的第一關鍵。
將咱們的高可用目標分解一下,其實有3個子目標:
1. 儘可能避免發生問題
不出問題固然是高可用的首要目標了,否則的話每天出問題,處理再快也沒意義。
2. 快速定位問題
出了問題要可以快速發現和定位,不要報警或者用戶投訴過來後還要花半天才能定位問題,在問題發生後儘快定位初步的緣由所在,儘快處理問題,防止問題惡化。
3. 快速恢復業務
特別注意這裏咱們強調的是「恢復業務」,而不是「解決問題」。不少人在處理生產問題或者故障的時候有一個誤區:那就是必定要找到問題根因,而後解決。實際上大部分的時候這樣作都很難的,也很耗費時間。好比說某個機器響應很慢,可能的緣由有:機器磁盤有問題、機器的CPU被耗光了、這臺機器上的程序陷入死循環、jvm垃圾回收時間較長......要在短短几分鐘內排查這麼多可能的緣由是很難的,但咱們不知道真正的緣由也能夠恢復業務,好比說最簡單的是直接把這臺機器馬上下線,讓流量分配到其它的機器。
當咱們審視這3個目標的時候,就會發現沒有哪一個系統的架構可以獨立的知足這目標,必須從業務的角度全方位、立體化的來分析和設計高可用方案。結合咱們的業務,最終的架構實現方案如圖1所示。
經過這個圖咱們能夠看到,這個架構設計方案並非傳統意義上的軟件架構,而是一個高可用的業務架構,真正和傳統系統架構相關的就只有「異地多活」,其它的功能或者系統並不屬於狹義上的架構設計範疇,但這些功能和系統組合起來,共同保障了業務的總體高可用。
當咱們的業務發生問題的時候,用戶側確定是感知最快的,若是可以在用戶側感知並馬上處理,問題的影響將大大下降。用戶側最簡單的處理問題就是重試,當遇到某些錯誤的時候用戶側再重試一次,錯誤能夠是通用的錯誤,例如HTTP 404/500,也能夠是業務上的錯誤碼,只須要客戶端和服務端預先約定便可。
用戶側重試是處理問題速度是最快的,但一個最大的問題就是DNS的不可靠性。簡單來講,若是經過DNS拿到了錯誤的主機地址,即便重試也同樣一樣是錯誤的。致使DNS錯誤的緣由主要有以下幾種:
1. 用戶側DNS被劫持,hosts被篡改
以下是咱們的域名在用戶機器上被篡改的實例,能夠看到咱們的域名被篡改爲了127.0.0.1。
2. 緩存DNS服務器污染,返回客戶端錯誤IP
2014年1月21日下午3點,國內頂級域的根服務器出現異常,許多知名網站的域名均被劫持到一個錯誤的IP地址上,至少有2/3的國內網站受到影響,用戶沒法正常訪問。根服務器恢復後,因爲DNS緩存問題,部分地區用戶「斷網」現象仍持續了幾個小時。
3. DNS緩存時間較長,短則10分鐘,長則幾小時
DNS的解析機制爲了提高效率,在不少地方會有緩存,例如本機的緩存,DNS服務器上的緩存。緩存帶來了效率上的提高,但同時卻給故障處理帶來了不小的麻煩,即:當咱們將故障的機器下線或者將DNS指向的主機地址修改之後,用戶並不能馬上感知新的主機地址,在緩存有效期內仍是會繼續訪問舊的主機。
DNS的這幾個問題雖然咱們都很清楚,可是也無能爲力,由於咱們不能控制DNS的設備,也不能修改DNS的實現機制。要想解決這個問題,只能另想辦法,咱們的解決方案就是HTTP-DNS。
故名思議,HTTP-DNS就是經過HTTP的方式來本身實現一套DNS的功能,簡單來講就是客戶端經過HTTP接口來獲取指定域名對應的主機地址,而再也不經過傳統的DNS設備來獲取主機地址。相比傳統DNS,HTTP-DNS具有以下優點:
1. 本身實現,控制力度強,能夠根據業務特色靈活實現
能夠根據業務進行細粒度的調度,例如發現A業務某個集羣請求量較多,能夠動態的將請求分配到其它集羣。
2. 更新快,故障處理及時
當更新域名對應的主機信息後,客戶端可以馬上拿到最新的信息。例以下線一臺機器後,客戶端再來獲取主機地址就不會獲取已經下線的機器地址,可以實現秒級的故障處理速度。
固然,HTTP-DNS的這些優點是在特定場景下才能體現的,並不能徹底取代傳統的DNS。由於若是每次訪問都先經過HTTP-DNS拿取主機地址的話,效率和性能都過低,對於手機類智能設備還會致使耗電和流量增長。所以咱們須要結合傳統DNS和HTTP-DNS的優點,既要保證大部分狀況下的性能和效率,也要保證異常狀況下的故障快速處理。
具體的作法爲:正常狀況下咱們經過傳統DNS完成業務請求,異常重試的時候經過HTTP-DNS完成請求。如圖3所示簡要的說明了總體流程。
咱們的高可用目標中,首要的目標就是「儘可能避免發生問題」,但對於如何避免發生問題,剛開始的時候團隊並無明確的方向,有的同窗從項目管理角度提出「結對編程」、「上線評審」等流程保障機制;也有同窗從測試角度提出「增強測試力度」、「自動化測試」等測試手段;咱們甚至還想到了「提高人員水平」這些人力資源措施......但這些措施都強依賴於人的因素,而人的因素每每是最不可控的,相比之下,咱們認爲經過技術的手段是更可控的,因此制定了「技術驅動」的總體策略。
經過對系統業務的仔細分析,以及對過往線上故障的問題的分析,咱們制定了「功能分離 + 功能降級」的方案,具體解釋以下;
1. 功能分離:劃分核心功能和非核心功能,將核心功能和非核心功能物理隔離
這裏有兩個關鍵點:
總體的架構示意見圖4。
2. 功能降級:當出現故障的時候,能夠將非核心功能直接降級,保護核心功能不受影響
拆分爲核心功能和非核心功能後,雖然物理上二者隔離了,但有的業務仍是須要核心功能和非核心功能配合才能完成,這就存在了必定的風險。好比說消息下發(非核心功能)須要獲取用戶的信息(核心功能),若是大量消息下發的話,就會給核心業務系統產生較大的壓力。這種狀況下咱們能夠經過將非核心功能停掉,以保證核心功能不受影響。
將某個功能停掉的傳統方式就是打patch,但作過的同窗都知道,這樣作的效率過低了:研發改代碼、測試驗證、運維部署,一路走下來,至少1小時以上,還容易出錯,並且過後還要打另一個patch恢復功能。爲了實現「5分鐘恢復業務」的目標,咱們開發了一個後臺管理程序,當須要停用某個功能的時候,只須要在後臺上點擊一個按鈕就可以完成,花費時間只須要幾秒鐘。
一般咱們講系統架構的時候,異地多活每每都是最吸引人的,由於異地多活看起來很美好,一個機房故障,其它機房可以徹底接管業務,故障處理簡單快速。但異地多活每每實現都是很複雜的,其複雜性不是體如今業務處理層面,而是體如今數據處理層面。簡單來講:異地多活須要保證數據的一致性和實時性,但異地機房間的距離自然決定了了數據的一致性和實時性是沒法保證的。
目前業界常見的異地多活方案整體上能夠分爲兩種:
1. 跨城多機房
在不一樣的城市搭建多個機房,機房間經過網絡進行數據複製(例如MySQL主備複製),但因爲跨城網絡時延的問題,業務上須要作必定的妥協和兼容,不須要數據的實時強一致性,保證最終一致性。
例如微博類產品,B用戶關注了A用戶,A用戶在北京機房發佈了一條微博,B在廣州機房不須要馬上看到A用戶發的微博,等10分鐘看到也能夠。
這種方式實現簡單,但和業務有很強的相關性,例如微博能夠這樣作,支付寶就不能這樣作。
2. 同城多機房
同一個城市多個機房,距離不會太遠,能夠投入重金,搭建私有的高速網絡,基本上可以作到和同機房同樣的效果。
這種方式對業務影響很小,但投入較大,若是不是土豪公司,通常是承受不起的;並且遇到極端的地震、2013年汕頭水災這樣天然災害,同城多機房也是有很大風險的。
咱們暫時還不是土豪,因此同城多機房就無法用了。同時,遊戲接入數據還要求實時強一致性,例如A用戶在北京機房註冊成功了,而後到廣州機房登陸游戲,要求可以馬上登陸,不可能讓用戶等10分鐘才能登陸,因此跨城多機房也不能用了。怎麼辦?方案彷佛陷入了僵局。
咱們轉向了第三種方式:由業務層來控制數據的強一致性和最終一致性,而不是由數據層來控制數據強一致性和最終一致性。具體來講就是3個手段:
1. 異步分發
一個機房生成的數據,會經過程序異步分發到其它機房,機房間沒有底層的數據複製通道,不存在主數據庫和備數據庫的概念,也就沒有主機房和備機房的概念,每一個機房都不依賴其它機房就能夠獨立提供服務。
經過這種異步分發的方式,能夠保證數據的最終一致性。當其中一個機房宕機時,除了宕機時刻尚未分發的數據外,其它數據在其它機房都已經有了,其它機房能夠接管宕機的機房的業務。
與傳統的MySQL複製相比,業務層數據分發能夠更加靈活,咱們能夠根據業務來選擇真正須要分發的數據,或者在分發前作一些預處理操做。業務層分發性能也能夠作到比MySQL更高,由於咱們能夠併發的進行分發。
2. 二次讀取
異步分發只能保證數據的最終一致性,不能保證數據的實時一致性,所以咱們設計了業務層二次讀取的機制。即:A機房讀取不到用戶的數據,則到B機房或者C機房去讀取。注意這裏的跨機房讀取操做都是經過接口,而不是經過訪問數據庫完成的。
經過二次讀取的方式,能夠保證數據的實時一致性。
3. 重複生成數據
對於全局惟一且又保證必須一致的數據,咱們不是依賴數據庫的自增id,或者zookeeper的全局id之類的機制,而是經過算法生成,這樣保證無論在哪一個機房均可以生成相同的數據。當其中一個機房宕機時,若是數據尚未分發到其它機房,此時雖然須要用戶從新操做一次,但因爲生成了相同的數據,業務上不會產生問題,用戶可以繼續正常使用業務。
而對於那些業務上能夠重複生成的數據,例如session、ticket類數據,處理就更加簡單了,不一樣機房間都不須要同步此類數據,若是沒有,直接二次生成便可。雖然要求用戶多登陸一次會帶來體驗上的不友好,但咱們的核心目標是不影響用戶玩遊戲,相比咱們的核心目標來講,這點影響微不足道。
在咱們最開始設計這套架構的時候,有的同窗憂慮地說「這也叫架構啊,看起來好像一點都不高大上哦」,但最後實現的效果大大超出了咱們的意料。經過上述三種手段,咱們實現了即便一個機房徹底宕機,另一個機房也能夠接管所有業務的目標。自從有了這套異地多活的架構,運維作機房切換很是簡單和輕鬆,有時候爲了驗證系統抗壓能力,咱們在正常的時候也將某個機房的業務所有切換到另一個機房,甚至運維同窗能夠開玩笑的說「沒事作的時候切換一下玩玩也能夠」!
處理過線上問題的朋友可能都有這樣的經歷:客戶投訴如今業務有問題,因而研發測試運維開始投入定位和分析問題,一場忙碌但又混亂的活動開始了。
A研發去查日誌,可是線上機器好多,一臺一臺的看,1小時過去了;若是想把日誌下載下來,一下幾個G的文件,網速又慢,只能乾等……
B研發同窗以爲數據庫可能有問題,可是本身又不能直接操做數據庫,只能找DBA,可是DBA不在線啊,趕忙打電話,DBA說我正好休假呢……
C運維同窗更頭大,一邊要應付研發和測試的各類問題,一邊還要本身查機器CPU、內存、io、網絡、程序狀態,並且還那麼多機器。
這樣的作法確定達不到「3分鐘定位問題」的要求,怎麼辦呢?咱們的運維大神給出了「立體化、自動化、可視化監控」的方向:在故障發生的時候,定位問題的所須要的各類信息都已經準備好了,不須要再到各類各樣的系統中去執行各類各樣的命令了;並且這些信息都必須是可視化的,可以一目瞭然的看出問題所在。具體實現以下:
1. 立體化
立體化就是將故障分析和定位時涉及的全部的相關信息都要監控起來,共分爲5層,具體各層和含義以下:
收集和分析業務層的訪問量、成功率等指標。例如當系統被刷的時候,業務層可以一目瞭然的看出訪問量會增長不少
應用服務層指的是以URI爲維度的分析,能夠看到某個URI的訪問量、HTTP響應碼分佈、HTTP響應時間等指標。應用服務層與業務層並非一 一對應的關係,一個業務可能對應多個應用服務層的URI,一個URI也可能對應多個業務層的業務。
接口調用層指的是系統依賴的外部系統接口,收集的信息包括訪問狀況,包括時延、錯誤碼、次數等,當外部系統故障致使咱們的業務故障時,經過接口調用層就可以快速的定位具體問題。
基礎組件層指系統依賴的底層組件,例如容器、數據庫、緩存、消息隊列。不一樣的組件收集的信息不同,例如數據庫MySQL的監控指標包括鏈接數、請求數、查詢行數、更新行數等,而緩存memcached包括使用率、踢出率、命中率等。
基礎設施層指操做系統狀態、網絡狀態,收集的信息包括cpu使用率、內存使用率、網卡流量、鏈接數等。
2. 自動化
自動化的含義就是不要再由人工去分析日誌或者執行命令了,而是由程序自動完成這些動做。當故障定位的時候須要這些信息時,能夠當即看到,節省故障定位時間。爲此咱們開發了一套數據收集和分析系統,這套系統能夠從其它各個系統(包括業務系統、運維繫統等)獲取並分析數據,例如日誌數據、狀態數據等。
數據自動化收集和分析系統的結構以下:
其中Logstash用於採集日誌,redis用於緩存日誌,elasticsearch用於存儲和分析日誌。
3. 可視化
可視化的含義就是故障定位所須要的信息可以經過圖表和數字直觀的展現出來。有了自動化的收集和分析做爲基礎,可視化只須要將數據作成圖表展現便可。除此之外,同比、環比這類數據也能夠經過系統直觀的展現出來,方便快速判斷問題所在。
咱們來回顧一下整個這套架構方案是如何實現咱們最初的目標的:
經過運維已有的撥測腳本,加上立體化&自動化&可視化的監控,絕大部分故障可以在3分鐘內發現並定位初步的緣由。例如是由於訪問量暴漲,仍是數據庫變慢了,仍是網絡被SYN FLOOD攻擊了。
若是是某臺機器或者某幾臺機器故障,咱們能夠將故障機器下線;
某個接口或者某些業務故障,若是不是核心業務,咱們能夠採用功能降級快速處理;
若是是大面積的業務故障,甚至是核心功能也故障了,咱們能夠經過將故障機房的業務切換到其它機房;
若是是全部的機房核心業務都出故障了,那就只能具體問題具體處理了,例如若是是新版本引發的,首先馬上回滾版本;若是是被攻擊了,須要採用防攻擊手段等。
經過核心功能和非核心功能分離,可以盡最大可能的保護核心功能的可靠性。固然除了這個措施外,咱們還有其它更多常規的保證措施,例如項目流程上的部署評審,自動化測試,灰度發佈,tcpcopy環境預發佈、MySQL高可用、Memcached高可用等措施。
架構設計主要參與的人員有:王金銀、王力志、聶勇、詹青朋、李運華、李俊,整個項目獲得了研發BOSS鄭從威的大力支持,這也是項目成功的關鍵因素!