滴滴出行分而治之的架構(gòu)設(shè)計(jì)之道
原創(chuàng)【本文是WOT2016互聯(lián)網(wǎng)運(yùn)維與開發(fā)者大會的現(xiàn)場干貨, 新一屆主題為WOT2016企業(yè)安全技術(shù)峰會將在2016年6月24日-25日于北京珠三角JW萬豪酒店隆重召開!】
如今,我們?nèi)ト魏我粋€(gè)地方都要先問問有沒有Wi-Fi,網(wǎng)絡(luò)已經(jīng)明顯影響到我們的生活。
互聯(lián)網(wǎng)生下來就是為了服務(wù)海量用戶,在這個(gè)時(shí)代,幾乎沒有哪個(gè)應(yīng)用再為單機(jī)而生。每個(gè)公司的每個(gè)產(chǎn)品將要面臨的都是不可預(yù)知的用戶海量請求。顯然這個(gè)靠分布式程序來解決,比依靠單機(jī)靠譜得多。然而不幸的是,如果一開始你的架構(gòu)設(shè)計(jì)不可擴(kuò)展,有再多的機(jī)器,有再多的云解決方案,對你來說至多是將單機(jī)程序跑在了一個(gè)虛擬的單機(jī)上。下面就讓我們回到WOT2016 互聯(lián)網(wǎng)運(yùn)維與開發(fā)者大會現(xiàn)場,跟隨滴滴出行***架構(gòu)師一起了解,分布時(shí)代架構(gòu)設(shè)計(jì)和程序開發(fā)面臨著哪些新挑戰(zhàn),以及滴滴出行的應(yīng)對思路。
李令輝,滴滴出行***架構(gòu)師,于2014年中加入滴滴,經(jīng)歷了滴滴高速成長的階段,見證了滴滴從一個(gè)打車軟件變成一個(gè)出行平臺。移動互聯(lián)網(wǎng)資深從業(yè)者,對移動互聯(lián)網(wǎng)技術(shù)發(fā)展趨勢以及技術(shù)團(tuán)隊(duì)的組建有獨(dú)道見解。他具有多年互聯(lián)網(wǎng)架構(gòu)的設(shè)計(jì)經(jīng)驗(yàn),擅長高性能高并發(fā)高可用的架構(gòu)設(shè)計(jì)工作,主導(dǎo)了滴滴打車技術(shù)迭代中的核心服務(wù)架構(gòu)升級。
分布式時(shí)代的困境
為單機(jī)而生的應(yīng)用將不復(fù)存在
很少有一個(gè)應(yīng)用能準(zhǔn)確預(yù)測自己的用戶量有多大,因此,一開始就為上億用戶去設(shè)計(jì)一個(gè)極為復(fù)雜的分布式架構(gòu),幾乎是不可能的。因?yàn)檫@不僅會帶來極高的成本,還會犧牲整個(gè)系統(tǒng)的靈活度。并不是每個(gè)公司都像谷歌一樣,在創(chuàng)業(yè)初期就有面對世界上所有數(shù)據(jù)的雄心壯志,來開發(fā)一個(gè)分布式文件系統(tǒng)。大多數(shù)公司一定是從幾臺服務(wù)器起家,在用戶不斷增長,并發(fā)請求增加,業(yè)務(wù)越來越復(fù)雜的過程中,百臨不得已將程序從單機(jī)搬到多臺機(jī)器。把單個(gè)進(jìn)程拆成多個(gè)服務(wù)的問題。
分布式開發(fā)工具的缺乏
每個(gè)人的工作量平白無故一個(gè)互聯(lián)網(wǎng)的多個(gè)節(jié)點(diǎn)組成的,通過網(wǎng)絡(luò)耦合的一個(gè)分布式環(huán)境。平白無故的被這種分布式帶來的必然復(fù)雜性提高了。但是,真正的分布式開發(fā)工具還遠(yuǎn)未成熟。 程序員可以使用的工具還是古老的VI,四十年前的Emacs和十幾年前的Eclipse等單機(jī)開發(fā)工具,服務(wù)之間的依賴關(guān)系完全無法管理,日志格式和日志內(nèi)容無法保證一致和可追溯。上線,擴(kuò)容,降級等運(yùn)維工作和規(guī)范沒有被很好的設(shè)計(jì)。 任何一次問題或者開發(fā),都需要多人協(xié)作,效率極為低下。
重造車輪的解決方案
看起來,業(yè)界解決方案百花爭鳴。但實(shí)際上,大部分都是基于開源的RPC方案,比較成型的幾個(gè)方案包括Erlang OTP, Scala Akka等。公司內(nèi)通過各種定制的方案去耦合,去互相管理關(guān)系,互相依賴,把一個(gè)事工作起來。大一點(diǎn)的公司會強(qiáng)制的推行運(yùn)維規(guī)范。而每個(gè)公司或者社區(qū)都對這種分布式環(huán)境用自己的理解。 這帶來的后果是,大家都在開源社區(qū)的基礎(chǔ)上重復(fù)造同樣的東西,這個(gè)是成本很高的事情。
再者,很多解決方案都依賴于特定的業(yè)務(wù)場景來制定。比如通訊軟件,對實(shí)時(shí)性要求很高,對可用性要求非常高,然而電商并不那么關(guān)心一個(gè)請求能不能快速返回,而是強(qiáng)調(diào)數(shù)據(jù)的一致性。所以每個(gè)業(yè)務(wù)特點(diǎn)決定了有不同的解決方案,而且很少有為分布式而生的方案,都是從單機(jī)方案演化或者漸變來的,這些問題都會讓每一個(gè)在從中開發(fā)的人不得不知道全貌,對研發(fā)效率來講是個(gè)巨大的傷害。分布式也確實(shí)個(gè)足夠復(fù)雜的領(lǐng)域,很難有一攬子通用解決方案。
那么,在設(shè)計(jì)分布式系統(tǒng)架構(gòu)時(shí),應(yīng)該考慮哪些方面?
分布式架構(gòu)設(shè)計(jì)基本要素
容錯(cuò)
在分布式環(huán)境里,錯(cuò)誤無處不在,并且無時(shí)無刻不在發(fā)生。而且,錯(cuò)誤不只是機(jī)器故障,當(dāng)幾百人投入研發(fā)工作的時(shí)候,一定會有人犯錯(cuò),而且每個(gè)人都會犯錯(cuò),會常態(tài)的犯錯(cuò)。因此,研發(fā)團(tuán)隊(duì)不應(yīng)該只想著如何避免錯(cuò)誤的發(fā)生,而是如何在小錯(cuò)誤下,不影響業(yè)務(wù),保持服務(wù)健康運(yùn)營。而一但不加考慮的對架構(gòu)每個(gè)模塊進(jìn)行降級,勢必帶來一場巨大的災(zāi)難。
數(shù)據(jù)格式
數(shù)據(jù)格式實(shí)際面臨的困境和依賴管理是一樣的。因?yàn)槊總€(gè)人只負(fù)責(zé)單獨(dú)的模塊,而不會去關(guān)心整個(gè)業(yè)務(wù)用什么樣的數(shù)據(jù)格式通信。究竟代碼中到底多少是用來Verify Data的?又有多少是用來Pack/Unpack Data的?如果不統(tǒng)一就會陷入泥潭,工作效率低到無法接受,日志收集和監(jiān)控也幾乎沒法實(shí)現(xiàn)。
路由層
關(guān)于路由層的解決方案沒有高下之分,只要能解業(yè)務(wù)中的問題,降低運(yùn)維成本和開發(fā)成本,就是好的方案。
但是,一定要盡量避免同時(shí)存在多種解決方案。函數(shù)調(diào)用是路由,反射是路由,URL是路由,RPC的IP+Port+Function也是路由。雖然說,并不是所有業(yè)務(wù)都能用統(tǒng)一的方法來路由的。路由的靈活性和規(guī)范性決定了運(yùn)維難度,盲目追求靈活度平白無故的又把運(yùn)維提的工作高一個(gè)量級。架構(gòu)本質(zhì)是控制復(fù)雜度,主要方法就是分而治之,解耦,耦合從本質(zhì)上來說就是路由。
服務(wù)
為了滿足用戶新的要求,追上市場新的步伐,每個(gè)互聯(lián)網(wǎng)公司的研發(fā)團(tuán)隊(duì)都不曾停下腳步,保證服務(wù)不斷進(jìn)化和升級。這同時(shí)也帶來了許多問題:
- 如何穩(wěn)定高效的迭代?
- 依賴剛迭代的服務(wù)的舊服務(wù)怎么辦?
- 我想給某個(gè)服務(wù)/模塊做AB Test怎么辦?
- 多個(gè)模塊可以同時(shí)做AB Test么?
- 如果不能,研發(fā)變成串行上線真的好么?
看待這些問題一定要從全局出發(fā),而最重要的是接口的統(tǒng)一,形成一致的標(biāo)準(zhǔn),讓大家在一條共同的準(zhǔn)繩上。
監(jiān)控
現(xiàn)在大家所做的監(jiān)控,基本都是在監(jiān)控機(jī)器的狀態(tài)。其實(shí)在幾百臺機(jī)器這樣的較小規(guī)模下,這樣做的意義并不大。真正應(yīng)該監(jiān)控的,應(yīng)該是程序。而嚴(yán)控程序的狀態(tài),只能依賴日志。
因此,每個(gè)架構(gòu)師都要考慮,如何設(shè)計(jì)可以監(jiān)控服務(wù)的日志系統(tǒng),要提供可監(jiān)控的接口。是每個(gè)架構(gòu)師要考慮你的服務(wù)是怎么被監(jiān)控的,你要提供可監(jiān)控的接口。至于采集間隔,一般來說規(guī)模越大,采集粒度越低,規(guī)模越小,采集粒度越高。
另外,監(jiān)控的信息是Pull or Push?監(jiān)控的結(jié)果全部需要人來處理么?日志是否可以用來作為系統(tǒng)之間交互的數(shù)據(jù)?這些問題都需要大家根據(jù)自己的業(yè)務(wù)場景不斷探索。
你的運(yùn)維方案***嗎?
每個(gè)公司的運(yùn)維團(tuán)隊(duì)都在考慮這個(gè)問題。你的目的是為了降低你的成本,提高你的效率。請合理的計(jì)算你的成本和效率,就是你要把人算進(jìn)去,而不是就算機(jī)器。大家可以通過以下幾個(gè)維度來評估:
- 資源利用率如何?對大部分團(tuán)隊(duì)來說,研發(fā)的人力成本要遠(yuǎn)遠(yuǎn)高于機(jī)器成本,你要首先考慮的是你的人都并發(fā)起來了,而不是你的CPU都被吃掉了
- 解決方案是否簡單?這對應(yīng)著人才招聘的門檻。對于新人來說,總要讓他快速的上手做一個(gè)項(xiàng)目,驗(yàn)證自己的能力,所以解決方案一定要相對簡單。怎么擴(kuò)容,怎么縮容,都應(yīng)該有成型的一整套方案
- 開發(fā)測試上線流程是否需要人工介入?
- 小流量測試的支持如何?
- 回滾、限流、斷流方案是否統(tǒng)一提供等等問題 ?
滴滴出行的分布式設(shè)構(gòu)設(shè)計(jì)思路
Linux之所以強(qiáng)大,是因?yàn)槊恳粋€(gè)模塊都只負(fù)責(zé)最簡單的事情,面對輸入和輸出,而輸入和輸出的格式是確定的。分布式架構(gòu)設(shè)計(jì)的思路也應(yīng)如此,同樣的規(guī)則,同樣的用法組合在一起是可以發(fā)揮巨大作用的。
滴滴出行的分布架構(gòu)設(shè)計(jì)想要解決的問題,不只是簡單的機(jī)器運(yùn)維,而是人在研發(fā)過程中,如何避免復(fù)雜環(huán)境中可能面臨的風(fēng)險(xiǎn),解決由于粗糙的架構(gòu)設(shè)計(jì)帶來的效率低下,不可控,不穩(wěn)定的狀態(tài)。
這樣的架構(gòu)設(shè)計(jì)帶來的一個(gè)巨大好處是,信息流在進(jìn)來的時(shí)候進(jìn)入信息分發(fā),信息分發(fā)把它分到合適的管道,那個(gè)管道處理完再放給下一個(gè)管道。每個(gè)管道都只做輸入和輸出的事情,實(shí)現(xiàn)高可用、高吞吐。這種方案很多云服務(wù)商都會提供。這樣做的好處時(shí)是,我們只需要管理消息隊(duì)列,可以在任意一個(gè)節(jié)點(diǎn)把流量復(fù)制走。在任何一個(gè)環(huán)節(jié)中可以拿到它所有的數(shù)據(jù),不再依賴日志,只依賴輸入、輸出。而輸入、輸出是存在硬盤上的,數(shù)據(jù)不會丟失。
另一個(gè)優(yōu)點(diǎn)是進(jìn)程是異步傳輸?shù)摹M侥P鸵粋€(gè)很明顯的缺點(diǎn)是在所有的層次中,一個(gè)進(jìn)程在執(zhí)行某個(gè)請求的時(shí)候如果需要一段時(shí)間才能返回信息,那么這個(gè)進(jìn)程將會一直等待下去,直到收到返回信息才繼續(xù)執(zhí)行下去。在流量很大的時(shí)候,做一個(gè)重試可能某一個(gè)環(huán)節(jié)就會面臨崩潰了,某個(gè)環(huán)節(jié)的連接數(shù)被打滿。
而在這個(gè)方案中,連接就只有兩三處,不需要等待數(shù)據(jù)回報(bào),只需要確認(rèn)收據(jù)接收,而且不需要逐條驗(yàn)證。成本很低,性能很高。
但這種架構(gòu)設(shè)計(jì)顯然不能解決所有的問題。比如用MySQL作為存儲等必須同步的服務(wù)時(shí),需要給有狀態(tài)的服務(wù)提供一個(gè)抽象層Service,上面的服務(wù)可以請求它。大家可以理解為在Linux中敲一個(gè)命令要讀一個(gè)文件,那個(gè)文件是有狀態(tài)的,是存在那里的,而這些模塊是沒有狀態(tài)的。
滴滴選擇了Docker+Kubernetes作為分布集群管理解決方案,它的好處是可以直接提供資源管理,資源隔離,部署,升級,路由等等需求。但是,只有Kubernetes是不夠的,Kubernetes只能管理那些無狀態(tài)的事務(wù)。并不是所有的事情都可以完全抽象成無狀態(tài)的,有狀態(tài)的部分應(yīng)該如何實(shí)現(xiàn)擴(kuò)容,都要依據(jù)具體的業(yè)務(wù)場景,這是很難的設(shè)計(jì)。
***要說的是,沒有***的方案,如果你自己要開發(fā)這個(gè)事情,建議大家***用一種方案,不要每一個(gè)用一種。但是沒辦法,面對不同的研發(fā)人員,不同的場景等現(xiàn)實(shí),現(xiàn)在還沒有最終的結(jié)論。也希望能借此文,與各位業(yè)界同仁共同探討。
【演講視頻】
分布式時(shí)代的架構(gòu)設(shè)計(jì)(上)
分布式時(shí)代的架構(gòu)設(shè)計(jì)(下)


























