NO IMAGE

今天給大家分享今日頭條架構演進,前面幾位講師講了很多具體的乾貨,我的分享偏重基礎設施及架構思路的介紹,我們想法是通過提供更好的基礎設施,幫助架構做更好的迭代。

從架構的角度,技術團隊應對的壓力最主要來自三方面:

  1. 服務穩定性。介面的穩定性,讓服務更可靠;
  2. 迭代速度。迭代速度對於大公司來講相對沒那麼重要,規模比較大,生存壓力相對小一點,但相對中型小型公司來講,迭代速度是必須要保證的,時間窗也是一個決定能否成功的重要因素;
  3. 服務質量。主要關注使用者滿意度,它也是一個特別重要的 topic。

今日頭條發展特別快,只有 4 年的歷史,從人員數量和規模增長來看非常快,在穩定性可用性方面壓力比較大,一方面需要快速把業務實現,但在另外一方面,類似這些高可用的問題會經常騷擾 工程師:上線就掛、運營活動量大服務崩潰、單機效能頂不住、一個小服務上線把核心服務搞掛了……類似這些問題,技術團隊需要如何更好的去應對?

先補充下我對架構演進的理解,在不同階段的公司都會面臨各種壓力。小公司壓力可能是業務沒起來,QPS 很低,要做優化也沒有環境及條件;當公司大了,伺服器可能已經不是問題,但你要不斷考慮調優及應對訪問壓力,改善基礎設施以提供更穩定的開發環境。所以說 架構演進是持續一個過程,沒有終點。

為什麼今日頭條有這麼大的壓力?今日頭條增長速度是比較快的,從上圖可以看出,現在公司已有 4 年,2014 到 2016 年每年都是 DAU 翻番。這對業務挑戰是非常大,規模上來以後,我們原有的架構難以做到線性擴充套件,部分能線性擴充套件的服務,問題也比較多,業務增長太快,後端壓力比較大。

頭條架構發展簡史:三個歷史階段

今日頭條的架構是怎麼發展過來的?

從來沒有一個完美的架構能夠一直支撐下去,架構是動態系統、實時變化的,因為量變而發生質的變化,不同的階段需要不同的架構。

什麼時候需要做架構上的改造呢?當突然發現系統問題越來越多,經常出現事故或者報警特別多,溝通的效率降低等問題,很有可能你的架構出現問題了。

軟體架構有一個問題,它改動的週期相對比較長。架構的模式思路定下來,隨著業務的增長,包袱越來越大。做過基礎設施的人都有這樣的體驗:有一個好的 想法很容易,但做一個好用軟體就有很多的困難。技術改造是漫長的,以年為單位。所以這個時候只能讓架構迭代更快一點。最後,不要企圖做特別完美的架構,我 們只要保持敏捷演進就好了。

架構不可避免會劣化。

頭條第一階段:三層結構

今日頭條剛開始做的時候,就是一個簡單 Web 應用,搭個資料庫,把業務實現就行了。頭條最開始的優勢是推薦引擎,還有另外一套資料探勘和離線計算。線上的服務在前端相對來講模式還是比較清晰的,三層 就搞定了。業務剛開始起來的時候,沒什麼問題,訪問增大水平擴充套件一下就可以解決。

頭條第二階段:拆分

跟大部分公司的架構演進歷史非常相似,當上個版本遇到一些效能問題後,最簡單就做一些拆分。優化的過程中,那一塊太重了就從程式碼上進行拆分。上圖中,A、B 和 C 是不同的業務,剛開始程式碼是一起的,演進的過程中,迭代一年或者兩年的產品,異構去拆其實挺痛苦。

前面時代的架構,基本上沒考慮太多的人員或者規模上的發展,剛開始也沒有專門的人做架構優化,很多人都撲在業務上,把功能點加上。比如推薦的效果不好,就加強推薦,每塊都沒有專門的的人去考慮整體架構去怎麼組織規劃。

到了去年,每個季度做的預算,到第二個月機器都用完了。高峰的時候有 60% 到 70% 的壓力,這裡涉及有兩個問題:第一個問題,有些地方是效能衰減的問題;另外一個,業務壓力太大。

架構團隊需要想辦法變得更快,即使出現訪問問題,壓力大,機器不夠,也要讓我們的服務有所保證。業務一直在快速前進,包袱是比較沉重,改造的成本比較高。基於這些問題,談下我們下一階段的思路,做微服務。

頭條第三階段:微服務

目前我們的思路是通過微服務方式做新架構。通過拆分成子系統,大的應用拆成小應用,抽象通用層做程式碼複用。

(點選圖片縮放大小)

系統的分層比較典型。我們重點在基礎設施,希望通過基礎設施提高快速迭代、容災和一系列的工作,希望各個業務團隊能更快做業務上的迭代以及架構上的調整。

微服務架構

微服務我們認為最關鍵的三點

  1. 解耦,一個服務會依賴另外一個服務、模組或子服務的概念。
  2. 輕量,減輕維護人員的成本。
  3. 易管理。

現實中微服務的關鍵是自治。雖然微服務是自治自包含,但也需要有一個層級。比如你提供的服務是外面公司提供的,微博提供的服務,你不能夠要求微博為你得服務去做更改。微服務要有界限,在公司層面,不能讓它過於獨立,過於獨立會增加溝通上的成本。基礎設施和規範最好能複用。

現實中的微服務是什麼樣的?

  • 架構必須要落地成具象的東西。微服務有一個開發框架,做業務的同學根本不需要關心容災,也不需要重複做這樣一套東西,這個東西怎麼部署,他們也不用關心;
  • 需要有一個流程規範性去約束。有規範就可以做全域性優化;
  • 微服務的表現形式是提供一個平臺或者一些工具。

頭條服務化的現在及未來

最後再給大家介紹前面服務化的思路在今日頭條是怎麼執行的?怎麼給各個業務團隊開發者提供服務?

頭條的主要服務化思路如下:

  • 立規範。規範怎麼做?部署 RPC,一個服務調另外一個服務是怎麼做的?創新我覺得沒有問題,但你得考慮給其他人帶來成本,這個規範還是需要有的,這樣可以做全域性控制。服務化的穩定和統一,你要考慮它帶來真正的優勢,效能高是一個點,但是本地優先會好一些;
  • 打基礎。有了規範以後,開始真正落地的服務。比如說基礎庫,把Ngnix、Redis、MySQL這些庫封裝起來,統一起來做一些事情。開發框架,你不用關注資料去優化;
  • 漸進。先拆離再迭代,把服務優化進來;
  • 一切都是服務,第四點是和其他公司或團隊稍不一樣的地方,我們的想法是一切都是服務,每個節點都是抽象歸屬於某一個具體的服務。儲存的確是一個服務,但它不只是提供 API 或者提供功能的東西,還需要包括服務質量,需要別人用起來是比較簡單的;
  • 平臺化。最後的落地是平臺化的東西。我們框架是怎麼設計,和服務怎麼結合?

首要規範:一切都是服務

  • 資源是有限的:按需申請,需申請和授權;
  • 簡單的使用方式:開發者只需要關注業務;
  • 有唯一定位的方式:用全域性資源定位;
  • 最後,每個服務都有擁有者(owner),偏工程架構方面的東西,我的規範必須可執行的。

我們的規範

  • 必須要有全域性的中心,服務統一註冊到 consul 中;
  • 服務有唯一的標示、命名範:{產品線}.{子系統}.{模組}  P.S.M,公司有很多部門,我們不希望部門之間溝通起來有差異,所以需要有全域性規劃去追溯它;
  • 業務服務使用 Thrift 描述介面、必須傳遞標準引數。如果用弱的描述資料,沒有強約束,在客戶端的資料可能會出現型別錯誤;
  • RPC 使用統一收斂的庫;
  • Nginx、Redis、MC、MySQL、etc 都是服務

服務註冊

我們服務統一使用 loader 或 wrapper 指令碼啟動,具體啟動由業務決定。

服務啟動會有一個名字,把 app 註冊到服務裡面,看起來有一些約束,資料庫MySQL 可以啟動嗎?Redis 可以嗎?

啟動的時候,服務方式不用去管,就用同一個框架,一個新的規範,容易把已有的服務遷移上來,但這不是個特別強的規範,考慮遷移成本。輕規範,易遷移。

服務中心

服務中心有服務資訊,會同時帶上是什麼樣的服務,其他人比較簡單的調這個服務就 OK 了。這個服務到底提供什麼樣的服務質量,擁有者可以管理這個資訊。Redis去服務,負載均衡,服務一個專案,把服務接上去。

服務關係與授權

服務之間有個關鍵的概念:服務授權。一般我們起一個服務,通過 IP 就可以連上它了。資料庫有使用者名稱認證,也可以對 IP 授權。不過內網很多服務限制比較少,不是所有服務都有授權認證。我們希望把服務之間的關聯關係,全域性拓撲關係記錄下來並且可執行。

一個服務提供介面,我們可以由 owner 來做授權,其他服務授權後才可以訪問它。

描述資訊:這個服務是什麼樣子?最大的 QPS 是多少?通過描述資訊發現問題,使用者資訊服務託不住了,就拒了,把資源分到其它服務上面,就可以做更多的東西。還有機房資訊可以放在這裡面。

服務授權認證思路:

  • 基於服務標示,重要服務增加更多認證方式;
  • 協同認證,客戶端自身協助認證。

舉一個 Thrift 的例子。這兩邊有兩個虛線,服務中心水平擴充套件能力很強,向它要基本授權的資訊,我可不可以調這個服務?預設是可以,就是一個 Thrift 包,我知道你是誰,自己做策略,服務包帶過來。請求帶上來,分析呼叫是不是有問題,這也是規範的一部分。開發的同學是不用關心框架這邊如何做的。

另外一種向服務中心呼叫服務,把你拒了。QPS 壓力大,已經支援不了你。一個好處是可以避免浪費資源;另外,虛擬化 Docker 的環節。以前的思路按 IP 授權,每一個 IP 做控制,提供類似於匿名服務,根據節點所屬 IP 去做。現在用 Docker 拿一個標識不太好做,在網路層也不太好做,在內網環境下有一定的可信,我自覺告訴你,我是誰,然後呼叫。

MySQL 目前正在做的一個方案見下圖,不像 Redis 要求帶上你是誰,呼叫 MySQL 需要把呼叫方是誰帶上來。一個重要資料庫,肯定做安全授權,我剛只是說常規情況下。這幾種方式疊加起來做,把原資訊帶過來,Redis 帶過來,做加權校驗。

Redis 在協議層做不了,而 MySQL 在呼叫中增加上述資訊不會影響語義。我們伺服器提供 HTTP 介面就可以在 HTTP 頭提供這個資訊做授權認證。

有授權關係,所有的服務構成完整服務的拓撲關係。一個服務預先授權才能調它。如果有線上真實的拓撲關係,就可以做報警優化。Redis 報警了,MySQL 報警了,有這樣的拓撲,會提升問題追查的速度。

我們有了這樣的拓撲資訊,知道服務的全域性元資訊,我們就可以更好的做服務變更的影響評估和報警等等的優化。

RPC 開發框架

我們自己開發了一個 RPC 框架。開發框架會幫助我們開發程式碼,這個事情很多人都在做。它的主要特性包括:

  • 快速開發:程式碼生成;
  • 服務發現:理解服務化;
  • 可觀測性(Observability):logid, pprof, admin 埠;
  • 容災降級:業務降級開關;
  • 過載保護:斷路器,頻率控制;
  • 多語言支援:Python/Go

比如可觀測性是說所有的服務都能暴露內部的狀態,這有非常好的優勢,服務上來以後,預設剖析內部的埠或者服務埠,服務上線與平臺。根據拓撲關係自動分析出來服務狀態,甚至做效能剖析,開發者可以不用關心這些事情,天然獲得這些能力。

還有容災降級,還有過載保護,我們還有一個平臺管理關聯關係和降級,你可以更多關注業務。

下面是大概模組的示意圖,通過模組化的方式,而不是嵌入到框架裡面,使我們的維護成本更低。

前面服務是自治的體現,跟 Docker 比較像,我們也會做容器化的開發。只是把服務跑在容器內還遠遠不夠,把服務化體系打通,我們思路實現開放,實現我們“有態度”的私有云,把基礎設施這一塊讓我們平臺做,業務部門只關心業務。

我們目前到這個環節,做一個服務的重構,我們的私有云構建。前面的框架,

不斷去迭代。

最後我們和虛擬化 PaaS 平臺怎麼規劃?

我們通過三層實現,通過 PaaS 平臺統一管理。提供通用 SaaS 服務,同時提供通用的 App 執行引擎。最底層是 IaaS 層。

IaaS 管理所有的機器,把公有云整合起來,頭條有一些熱點事件會全國推廣推送,對網路頻寬比較高,我們藉助公有云,需要哪一種型別計算資源,統一抽象起來。基礎設施結合服務化的思路,比如日誌,監控等等功能,業務不需要關注細節就可以享受到基礎設施提供的能力。

Q&A

Q:剛才講單體服務拆分成微服務成本是不是有所增加,你怎麼考慮?

夏緒巨集:我在過去建一個資料庫,直接跑起來。以前把整個庫升級,現在只需要升級一小部分,業務比較簡單規模比較小的時候單體服務確實成本低。當你的 業務增大機器增多,單體服務會成為瓶頸,但是微服務如果是標準化的,可以用自動化的工具、平臺去管理,不能靠人去管理,因此成本反而是降低的。

Q:把服務跑在容器裡面,用了 consul,自身的容器和 IP 等自身的資訊註冊到consul,更新您授權的 ACL?

夏緒巨集:這的確是一個思路,我們用了 consul 是去中心化,不過也是增加了一層。如果你需要控制微服務的訪問與安全,容器節點還要分級別,比如我會分到小叢集,物理層是隔離,以這種方式實現安全的。僅是 consul 是不夠的。

Q:RPC 服務發現是什麼?RPC 是自己實現的?

夏緒巨集:服務發現是 consul;RPC 是自己在 Thrift 基礎上實現,服務呼叫上也實現了熔斷機制。

Q:選型的時候為什麼不用開源,我們是整個平臺的架構準備做微服務的改造,想選一下服務的架構,您這塊是自己做的,我們在開源和自己做之間在選。能舉一個例子?

夏緒巨集:看場景,你們現在什麼都沒有可以考慮各種開源方案,我們也有一些自己特殊場景,開源的東西跟內部服務做整合,需要考慮一些整合成本和我們自 己維護的成本。很多時候開源專案會出於普適性,會考慮的特性比較多一些,程式碼也會相對複雜,有些功能我們用不到,我們要去做改造,整體上不復雜;

授權標準也是自己做的,基於服務標識,伺服器裡面,並沒有考慮場景互聯。

Q:我們現在如果做微服務平臺改造,業務系統開發模式是不是有比較大的改變?我們平臺從開發模式到設計都會有所改變,你們改造過了,你們有哪些經驗?

夏緒巨集:我們改造到現在也沒改造完,這個改造挺困難的。你先定好一個大方向,因為很多東西是涉及到溝通的問題,推動的問題,你先需要溝通好的方向,達成一致,到怎麼改造,機動性少一點,或者你把大部分的功能實現好,只需要做小小的遷移,減少遷移的成本。