夏緒宏,今日頭條架構(gòu)師,專注對高性能大規(guī)模 Web 架構(gòu),云計算、性能優(yōu)化、編程語言理論等方向,PHP committer,HHVM 項目貢獻(xiàn)者。2009 加入百度,先后從事大規(guī)模 IDC 自運維設(shè)施建設(shè)、云計算平臺的架構(gòu)設(shè)計、貼吧業(yè)務(wù)性能優(yōu)化、百度通用 RPC 設(shè)計和優(yōu)化等。2015 年加入今日頭條負(fù)責(zé)基礎(chǔ)設(shè)施,系統(tǒng)架構(gòu)設(shè)計和優(yōu)化,解決大流量高并發(fā)下的系統(tǒng)性能、可靠性和運維效率等方面的問題。 今天給大家分享今日頭條架構(gòu)演進(jìn),我的分享偏重基礎(chǔ)設(shè)施及架構(gòu)思路的介紹,我們想法是通過提供更好的基礎(chǔ)設(shè)施,幫助架構(gòu)做更好的迭代。 從架構(gòu)的角度,技術(shù)團(tuán)隊?wèi)?yīng)對的壓力最主要來自三方面: -
服務(wù)穩(wěn)定性 。接口的穩(wěn)定性,讓服務(wù)更可靠; -
迭代速度 。迭代速度對于大公司來講相對沒那么重要,規(guī)模比較大,生存壓力相對小一點,但相對中型小型公司來講,迭代速度是必須要保證的,時間窗也是一個決定能否成功的重要因素; -
服務(wù)質(zhì)量 。主要關(guān)注用戶滿意度,它也是一個特別重要的 topic。 今日頭條發(fā)展特別快,只有 4 年的歷史,從人員數(shù)量和規(guī)模增長來看非???,在穩(wěn)定性可用性方面壓力比較大,一方面需要快速把業(yè)務(wù)實現(xiàn),但在另外一方面,類似這些高可用的問題會經(jīng)常騷擾工程師:上線就掛、運營活動量大服務(wù)崩潰、單機(jī)性能頂不住、一個小服務(wù)上線把核心服務(wù)搞掛了……類似這些問題,技術(shù)團(tuán)隊需要如何更好的去應(yīng)對? 先補(bǔ)充下我對架構(gòu)演進(jìn)的理解,在不同階段的公司都會面臨各種壓力。小公司壓力可能是業(yè)務(wù)沒起來,QPS 很低,要做優(yōu)化也沒有環(huán)境及條件;當(dāng)公司大了,服務(wù)器可能已經(jīng)不是問題,但你要不斷考慮調(diào)優(yōu)及應(yīng)對訪問壓力,改善基礎(chǔ)設(shè)施以提供更穩(wěn)定的開發(fā)環(huán)境。所以說架構(gòu)演進(jìn)是持續(xù)一個過程,沒有終點。 為什么今日頭條有這么大的壓力?今日頭條增長速度是比較快的,從上圖可以看出,現(xiàn)在公司已有 4 年,2014 到 2016 年每年都是 DAU 翻番。這對業(yè)務(wù)挑戰(zhàn)是非常大,規(guī)模上來以后,我們原有的架構(gòu)難以做到線性擴(kuò)展,部分能線性擴(kuò)展的服務(wù),問題也比較多,業(yè)務(wù)增長太快,后端壓力比較大。 頭條架構(gòu)發(fā)展簡史:三個歷史階段 今日頭條的架構(gòu)是怎么發(fā)展過來的? 從來沒有一個完美的架構(gòu)能夠一直支撐下去,架構(gòu)是動態(tài)系統(tǒng)、實時變化的,因為量變而發(fā)生質(zhì)的變化, 不同的階段需要不同的架構(gòu) 。 什么時候需要做架構(gòu)上的改造呢?當(dāng)突然發(fā)現(xiàn)系統(tǒng)問題越來越多,經(jīng)常出現(xiàn)事故或者報警特別多,溝通的效率降低等問題,很有可能你的架構(gòu)出現(xiàn)問題了。 軟件架構(gòu)有一個問題,它改動的周期相對比較長。架構(gòu)的模式思路定下來,隨著業(yè)務(wù)的增長,包袱越來越大。做過基礎(chǔ)設(shè)施的人都有這樣的體驗:有一個好的想法很容易,但做一個好用軟件就有很多的困難。技術(shù)改造是漫長的,以年為單位。所以這個時候只能讓架構(gòu)迭代更快一點。最后,不要企圖做特別完美的架構(gòu),我們只要保持敏捷演進(jìn)就好了。 架構(gòu)不可避免會劣化。 頭條第一階段:三層結(jié)構(gòu) 今日頭條剛開始做的時候,就是一個簡單 Web 應(yīng)用,搭個數(shù)據(jù)庫,把業(yè)務(wù)實現(xiàn)就行了。頭條最開始的優(yōu)勢是推薦引擎,還有另外一套數(shù)據(jù)挖掘和離線計算。在線的服務(wù)在前端相對來講模式還是比較清晰的,三層就搞定了。業(yè)務(wù)剛開始起來的時候,沒什么問題,訪問增大水平擴(kuò)展一下就可以解決。 頭條第二階段:拆分 跟大部分公司的架構(gòu)演進(jìn)歷史非常相似,當(dāng)上個版本遇到一些性能問題后,最簡單就做一些拆分。優(yōu)化的過程中,那一塊太重了就從代碼上進(jìn)行拆分。上圖中,A、B 和 C 是不同的業(yè)務(wù),剛開始代碼是一起的,演進(jìn)的過程中,迭代一年或者兩年的產(chǎn)品,異構(gòu)去拆其實挺痛苦。 前面時代的架構(gòu),基本上沒考慮太多的人員或者規(guī)模上的發(fā)展,剛開始也沒有專門的人做架構(gòu)優(yōu)化,很多人都撲在業(yè)務(wù)上,把功能點加上。比如推薦的效果不好,就加強(qiáng)推薦,每塊都沒有專門的的人去考慮整體架構(gòu)去怎么組織規(guī)劃。 到了去年,每個季度做的預(yù)算,到第二個月機(jī)器都用完了。高峰的時候有 60% 到 70% 的壓力,這里涉及有兩個問題:第一個問題,有些地方是性能衰減的問題;另外一個,業(yè)務(wù)壓力太大。 架構(gòu)團(tuán)隊需要想辦法變得更快,即使出現(xiàn)訪問問題,壓力大,機(jī)器不夠,也要讓我們的服務(wù)有所保證。業(yè)務(wù)一直在快速前進(jìn),包袱是比較沉重,改造的成本比較高?;谶@些問題,談下我們下一階段的思路,做微服務(wù)。 頭條第三階段:微服務(wù) 目前我們的思路是通過微服務(wù)方式做新架構(gòu)。通過拆分成子系統(tǒng),大的應(yīng)用拆成小應(yīng)用,抽象通用層做代碼復(fù)用。 系統(tǒng)的分層比較典型。我們重點在基礎(chǔ)設(shè)施,希望通過基礎(chǔ)設(shè)施提高快速迭代、容災(zāi)和一系列的工作,希望各個業(yè)務(wù)團(tuán)隊能更快做業(yè)務(wù)上的迭代以及架構(gòu)上的調(diào)整。 微服務(wù)架構(gòu) 微服務(wù)我們認(rèn)為最關(guān)鍵的三點 -
解耦,一個服務(wù)會依賴另外一個服務(wù)、模塊或子服務(wù)的概念。 -
輕量,減輕維護(hù)人員的成本。 -
易管理。 現(xiàn)實中微服務(wù)的關(guān)鍵是自治。雖然微服務(wù)是自治自包含,但也需要有一個層級。比如你提供的服務(wù)是外面公司提供的,微博提供的服務(wù),你不能夠要求微博為你得服務(wù)去做更改。微服務(wù)要有界限,在公司層面,不能讓它過于獨立,過于獨立會增加溝通上的成本?;A(chǔ)設(shè)施和規(guī)范最好能復(fù)用。 現(xiàn)實中的微服務(wù)是什么樣的? -
架構(gòu)必須要落地成具象的東西。微服務(wù)有一個開發(fā)框架,做業(yè)務(wù)的同學(xué)根本不需要關(guān)心容災(zāi),也不需要重復(fù)做這樣一套東西,這個東西怎么部署,他們也不用關(guān)心; -
需要有一個流程規(guī)范性去約束。有規(guī)范就可以做全局優(yōu)化; -
微服務(wù)的表現(xiàn)形式是提供一個平臺或者一些工具。 頭條服務(wù)化的現(xiàn)在及未來 最后再給大家介紹前面服務(wù)化的思路在今日頭條是怎么執(zhí)行的?怎么給各個業(yè)務(wù)團(tuán)隊開發(fā)者提供服務(wù)? 頭條的主要服務(wù)化思路如下: -
立規(guī)范 。規(guī)范怎么做?部署 RPC,一個服務(wù)調(diào)另外一個服務(wù)是怎么做的?創(chuàng)新我覺得沒有問題,但你得考慮給其他人帶來成本,這個規(guī)范還是需要有的,這樣可以做全局控制。服務(wù)化的穩(wěn)定和統(tǒng)一,你要考慮它帶來真正的優(yōu)勢,性能高是一個點,但是本地優(yōu)先會好一些; -
打基礎(chǔ) 。有了規(guī)范以后,開始真正落地的服務(wù)。比如說基礎(chǔ)庫,把 Ngnix、Redis、MySQL 這些庫封裝起來,統(tǒng)一起來做一些事情。開發(fā)框架,你不用關(guān)注數(shù)據(jù)去優(yōu)化; -
漸進(jìn) 。先拆離再迭代,把服務(wù)優(yōu)化進(jìn)來; -
一切都是服務(wù) ,第四點是和其他公司或團(tuán)隊稍不一樣的地方,我們的想法是一切都是服務(wù),每個節(jié)點都是抽象歸屬于某一個具體的服務(wù)。存儲的確是一個服務(wù),但它不只是提供 API 或者提供功能的東西,還需要包括服務(wù)質(zhì)量,需要別人用起來是比較簡單的; -
平臺化 。最后的落地是平臺化的東西。我們框架是怎么設(shè)計,和服務(wù)怎么結(jié)合? 首要規(guī)范:一切都是服務(wù) -
資源是有限的:按需申請,需申請和授權(quán); -
簡單的使用方式:開發(fā)者只需要關(guān)注業(yè)務(wù); -
有唯一定位的方式:用全局資源定位; -
最后,每個服務(wù)都有擁有者(owner),偏工程架構(gòu)方面的東西,我的規(guī)范必須可執(zhí)行的。 我們的規(guī)范 -
必須要有全局的中心,服務(wù)統(tǒng)一注冊到 consul 中; -
服務(wù)有唯一的標(biāo)示、命名范:{產(chǎn)品線}.{子系統(tǒng)}.{模塊} P.S.M,公司有很多部門,我們不希望部門之間溝通起來有差異,所以需要有全局規(guī)劃去追溯它; -
業(yè)務(wù)服務(wù)使用 Thrift 描述接口、必須傳遞標(biāo)準(zhǔn)參數(shù)。如果用弱的描述數(shù)據(jù),沒有強(qiáng)約束,在客戶端的數(shù)據(jù)可能會出現(xiàn)類型錯誤; -
RPC 使用統(tǒng)一收斂的庫; -
Nginx、Redis、MC、MySQL、etc 都是服務(wù) 服務(wù)注冊 我們服務(wù)統(tǒng)一使用 loader 或 wrapper 腳本啟動,具體啟動由業(yè)務(wù)決定。 服務(wù)啟動會有一個名字,把 app 注冊到服務(wù)里面,看起來有一些約束,數(shù)據(jù)庫 MySQL 可以啟動嗎?Redis 可以嗎? 啟動的時候,服務(wù)方式不用去管,就用同一個框架,一個新的規(guī)范,容易把已有的服務(wù)遷移上來,但這不是個特別強(qiáng)的規(guī)范,考慮遷移成本。 輕規(guī)范,易遷移。 服務(wù)中心 服務(wù)中心有服務(wù)信息,會同時帶上是什么樣的服務(wù),其他人比較簡單的調(diào)這個服務(wù)就 OK 了。這個服務(wù)到底提供什么樣的服務(wù)質(zhì)量,擁有者可以管理這個信息。Redis 去服務(wù),負(fù)載均衡,服務(wù)一個項目,把服務(wù)接上去。 服務(wù)關(guān)系與授權(quán) 服務(wù)之間有個關(guān)鍵的概念:服務(wù)授權(quán)。一般我們起一個服務(wù),通過 IP 就可以連上它了。數(shù)據(jù)庫有用戶名認(rèn)證,也可以對 IP 授權(quán)。不過內(nèi)網(wǎng)很多服務(wù)限制比較少,不是所有服務(wù)都有授權(quán)認(rèn)證。我們希望把服務(wù)之間的關(guān)聯(lián)關(guān)系,全局拓?fù)潢P(guān)系記錄下來并且可執(zhí)行。 一個服務(wù)提供接口,我們可以由 owner 來做授權(quán),其他服務(wù)授權(quán)后才可以訪問它。 描述信息:這個服務(wù)是什么樣子?最大的 QPS 是多少?通過描述信息發(fā)現(xiàn)問題,用戶信息服務(wù)托不住了,就拒了,把資源分到其它服務(wù)上面,就可以做更多的東西。還有機(jī)房信息可以放在這里面。 服務(wù)授權(quán)認(rèn)證思路: 舉一個 Thrift 的例子。這兩邊有兩個虛線,服務(wù)中心水平擴(kuò)展能力很強(qiáng),向它要基本授權(quán)的信息,我可不可以調(diào)這個服務(wù)?默認(rèn)是可以,就是一個 Thrift 包,我知道你是誰,自己做策略,服務(wù)包帶過來。請求帶上來,分析調(diào)用是不是有問題,這也是規(guī)范的一部分。開發(fā)的同學(xué)是不用關(guān)心框架這邊如何做的。 另外一種向服務(wù)中心調(diào)用服務(wù),把你拒了。QPS 壓力大,已經(jīng)支持不了你。一個好處是可以避免浪費資源;另外,虛擬化 Docker 的環(huán)節(jié)。以前的思路按 IP 授權(quán),每一個 IP 做控制,提供類似于匿名服務(wù),根據(jù)節(jié)點所屬 IP 去做?,F(xiàn)在用 Docker 拿一個標(biāo)識不太好做,在網(wǎng)絡(luò)層也不太好做,在內(nèi)網(wǎng)環(huán)境下有一定的可信,我自覺告訴你,我是誰,然后調(diào)用。 MySQL 目前正在做的一個方案見下圖,不像 Redis 要求帶上你是誰,調(diào)用 MySQL 需要把調(diào)用方是誰帶上來。一個重要數(shù)據(jù)庫,肯定做安全授權(quán),我剛只是說常規(guī)情況下。這幾種方式疊加起來做,把原信息帶過來,Redis 帶過來,做加權(quán)校驗。 Redis 在協(xié)議層做不了,而 MySQL 在調(diào)用中增加上述信息不會影響語義。我們服務(wù)器提供 HTTP 接口就可以在 HTTP 頭提供這個信息做授權(quán)認(rèn)證。 有授權(quán)關(guān)系,所有的服務(wù)構(gòu)成完整服務(wù)的拓?fù)潢P(guān)系。一個服務(wù)預(yù)先授權(quán)才能調(diào)它。如果有線上真實的拓?fù)潢P(guān)系,就可以做報警優(yōu)化。Redis 報警了,MySQL 報警了,有這樣的拓?fù)?,會提升問題追查的速度。 我們有了這樣的拓?fù)湫畔ⅲ婪?wù)的全局元信息,我們就可以更好的做服務(wù)變更的影響評估和報警等等的優(yōu)化。 RPC 開發(fā)框架 我們自己開發(fā)了一個 RPC 框架。開發(fā)框架會幫助我們開發(fā)代碼,這個事情很多人都在做。它的主要特性包括: -
快速開發(fā):代碼生成; -
服務(wù)發(fā)現(xiàn):理解服務(wù)化; -
可觀測性(Observability):logid, pprof, admin 端口; -
容災(zāi)降級:業(yè)務(wù)降級開關(guān); -
過載保護(hù):斷路器,頻率控制; -
多語言支持:Python/Go 前面服務(wù)是自治的體現(xiàn),跟 Docker 比較像,我們也會做容器化的開發(fā)。只是把服務(wù)跑在容器內(nèi)還遠(yuǎn)遠(yuǎn)不夠,把服務(wù)化體系打通,我們思路實現(xiàn)開放,實現(xiàn)我們“有態(tài)度”的私有云,把基礎(chǔ)設(shè)施這一塊讓我們平臺做,業(yè)務(wù)部門只關(guān)心業(yè)務(wù)。 我們目前到這個環(huán)節(jié),做一個服務(wù)的重構(gòu),我們的私有云構(gòu)建。前面的框架, 不斷去迭代。 最后我們和虛擬化 PaaS 平臺怎么規(guī)劃? 我們通過三層實現(xiàn),通過 PaaS 平臺統(tǒng)一管理。提供通用 SaaS 服務(wù),同時提供通用的 App 執(zhí)行引擎。最底層是 IaaS 層。 IaaS 管理所有的機(jī)器,把公有云整合起來,頭條有一些熱點事件會全國推廣推送,對網(wǎng)絡(luò)帶寬比較高,我們借助公有云,需要哪一種類型計算資源,統(tǒng)一抽象起來?;A(chǔ)設(shè)施結(jié)合服務(wù)化的思路,比如日志,監(jiān)控等等功能,業(yè)務(wù)不需要關(guān)注細(xì)節(jié)就可以享受到基礎(chǔ)設(shè)施提供的能力。 Q&A
|