LinkedIn架構演化歷史解析

yeeyan發表於2015-07-25

LinkedIn 建立於 2003 年,主要目標是連線你的個人人脈以得到更好的的工作機會。上線第一週只有 2700 個會員,之後幾年,LinkedIn 的產品、會員、伺服器負載都增長非常快。

今天,LinkedIn 全球使用者已經超過 3.5 億。我們每天每秒有上萬個頁面被訪問,移動端流量已佔到 50% 以上。所有這些介面請求都從後臺獲取,達到每秒上百萬級。

那麼,我們是怎麼做到的呢?

早些年 – Leo

LinedIn 開始跟很多網站一樣,只有一臺應用服務做了全部工作。這個應用我們給它取名叫“Leo”。它包含了所有的網站頁面(JAVA Servlets),還包含了業務邏輯,同時連線了一個輕量的 LinkedIn 資料庫。

哈!早年網站的形態-簡單實用

會員圖表

第一件要做的是管理會員與會員間的社交網路。我們需要一個系統來圖形化遍歷使用者之間連線的資料,同時又駐留記憶體以達到有效和高效能。從這個不同的需求來看,很明顯我們需要可以獨立可擴充套件的 Leo。一個獨立的會員圖示化系統,叫“雲”的服務誕生了 – LinkedIn 的第一個服務。為了讓圖表服務獨立於 Leo,我們使用了 Java RPC 用來通訊。

也大概在這期間我們也有搜尋服務的需求了,同時會員圖表服務也在給搜尋引擎-Lucene 提供資料。

複製只讀資料庫

當站點繼續增長,Leo 也在增長,增加了角色和職責,同時自然也增加了複雜度。負載均衡的多例項 Leo 運轉起來了。但新增的負載也影響了 LinkedIn 的其它關鍵系統,如會員資訊資料庫。

一個通常的解決方案是做垂直擴充套件 – 即增加更多的 CPU 和記憶體!雖然換取了時間,但我們以後還要擴充套件。會員資訊資料庫受理了讀和寫請求,為了擴充套件,一個複製的從資料庫出現了,這個複製從庫, 是會員主庫的一個拷貝,用早期的 databus 來保證資料的同步(現在開源了)。從庫接管了所有的讀請求,同時新增了保證從庫與主庫一致的邏輯。

當主-從架構工作了一段時間後,我們轉向了資料庫分割槽

當網站開始看起來越來越多流量,我們的應用服務 Leo 在生產環境經常當機,排查和恢復都很困難,釋出新程式碼也很困難,高可用性對 LinkedIn 至關重要,很明顯我們要“幹掉”Leo,然後把它拆分成多個小功能塊和無狀態服務。

面向服務的架構 – SOA

工程師從分解負擔 API 介面和業務邏輯的微服務開始,如搜尋、個人資訊、通訊及群組平臺,然後是展示層分解,如招募功能和公共介紹頁。新產品和新服務都放在 Leo 之外了,不久,每個功能區的垂直服務棧完成了。

我們構建了從不同域抓取資料模型的前端伺服器,用於表現層展示,如生成 HTML(通過 JSPs)。我們還構建了中間層服務提供 API 介面訪問資料模型及提供資料庫一致性訪問的後端資料服務。到 2010 年,我們已經有超過 150 個單獨的服務,今天,我們已經有超過 750 個服務。

因為無狀態,可以直接堆疊新服務例項及用硬體負載均衡實現擴充套件。我們給每個服務都畫了警戒紅線,以便知道它的負載,從而制定早期對策和效能監控。

快取

LinkedIn 可預見的增長,所以還需要進一步擴充套件。我們知道通過新增更多快取可以減少集中壓力的訪問。很多應用開始使用如 memcached/couchbase 的中間層快取,同時在資料層也加了快取,某些場景開始使用 useVoldemort 提供預結果生成。

又過一了段時間,我們實際上去掉了很多中間層快取,中間層快取資料往往來自多個域,但快取只是開始時對減少負載有用,但更新快取的複雜度和生成圖表變得更難於把控。保持快取最接近資料層將降低潛在的不可控影響,同時還允許水平擴充套件,降低分析的負載。

Kafka

為了收集不斷增長的資料,LinkedIn 開始了很多自定義的資料管道來流化和佇列化資料,例如,我們需要把資料儲存到資料倉儲、我們需要傳送批量資料到 Hadoop 工作流以分析、我們從每個服務聚合了大量日誌、我們跟蹤了很多使用者行為,如頁面點選、我們需要隊例化 InMail 訊息服務、我們要保障當使用者更新個人資料後搜尋的資料是最新的等等。

當站點還在增長,更多定製化管道服務出現了,因網站需要可擴充套件,單獨的管道也要可擴充套件,有些時候很難取捨。解決方案是使用 kafka,我們的分散式釋出-訂閱訊息平臺。Kafka 成為一個統一的管道服務,根據提交的日誌生成摘要,同時一開始就很快速和具有可擴充套件性。它可以接近於實時的訪問所有資料來源,驅動了 Hadoop 任務,允許我們構建實時的分析,廣泛的提升了我們站點監控和告警的能力,同時支援將呼叫視覺化。今天,Kafka 每天處理超過 5 億個事件。

反轉

擴充套件可從多個維度來衡量,包括組織結構。2011 年晚些時候,LinkedIn 內部開始一個創新,叫”反轉”(Inversion)。我們暫停了新功能開發,允許所有的開發部門集中注意力提升工具和佈署、基礎架構及實用性開發。這對於今天敏捷開發新的可擴充套件性產品很有幫助。

近幾年 – Rest.li

當我們從 Leao 轉向面向服務的架構後,之前基於 JAVA 的 RPC 介面,在團隊中已經開始分裂了,並且跟表現層綁得太死,這,只會變得更壞。為了搞定這個問題,我們開發了一套新的 API 模型,叫 Rest.li,它是一套以資料為中心的架構,同時保證在整個公司業務的資料一致性且無狀態的 Restful API。

基於 HTTP 的 JSON 資料傳遞,我們新的 API 最終很容易支援到非 java 編寫的客戶端,LinkedIn 今天依然主要用 Java,但同時根據已有的技術分佈也利用了 Python、Ruby、Node.js 和C++。脫離了 RPC,同時也讓我們從前端表現層及後端相容問題解耦,另外, 使用了基於動態發現技術(D2)的 Rest.li,我們每個服務層 API 獲得了自動負載均衡、發現和擴充套件的能力。

今天,LinkedIn 所有資料中心每天已經有超過 975 個 Rest.li 資源和超過千億級 Rest.li 呼叫。

Rest.li R2/D2 技術棧

超級塊

面向服務的架構對於域解耦和服務獨立擴充套件性很有幫助,但弊端是,大都我們的應用都需要很多不同型別的資料,按序會產品數百個延伸的呼叫。這就是通常說的“呼叫線路圖”,或伴隨著這麼多延伸呼叫的“扇出”(fan-out)。例如,任何個人資訊頁都包含了遠不止於相簿、連線、群組、訂閱、關注、部落格、人脈維度、推薦這些。呼叫圖表可能會很難管理,而且只會把事件搞得越來越不規則。

我們使用了”超級塊”的概念 – 一個只有單一 API 介面的群組化後臺服務。這樣可以允許一個小組去優化一個“塊”,同時保證每個客戶端的呼叫情況可控。

多資料中心

作為一個會員快速增長的全球化公司,我們需要將資料中心進行擴充套件,我們努力了幾年來解決這個問題,首先,從兩個資料中心(洛杉磯和芝加哥)提供了公共個人資訊,這表明,我們已經可以提供增強的服務用來資料複製、不同源的遠端呼叫、單獨資料複製事件、將使用者分配到地理位置更近的資料中心。

我們大多的資料庫執行在 Espresso(一個新的內部多使用者資料倉儲)。Espresso 支援多個資料中心,提供了主-主的支援,及支援複雜的資料複製。

多個資料中心對於高可用性具有不可思議的重要性,你要避免因單點故障不僅只導致某個服務失效,更要擔心整個站點失效。今天,LinkedIn 執行了 3 個主資料中心,同時還有全球化的 PoPs 服務。

我們還做了哪些工作?

當然,我們的擴充套件故事永遠不止這麼簡單。我們的工程師和運維團隊這些年做了不計其數的工作,主要包括這些大的初創性的:

這些年大多關鍵系統都有自己的豐富的擴充套件演化歷史,包括會員圖表服務(Leo 之外的第一個服務),搜尋(第二個服務),新聞種子,通訊平臺及會員資料後臺。

我們還構建了資料基礎平臺支援很長一段時間的增長,這是 Databus 和 Kafka 的第一次實戰,後來用 Samza 做資料流服務,Espresso 和 Voldemort 作儲存解決方案,Pinot 用來分析系統,以及其它自定義解決方案。另外,我們的工具也進步了,如工程師可自動化佈署這些基礎架構。

我們還使用 Hadoop 和 Voldemort 資料開發了大量的離線工作流,用以智慧分析,如“你可能認識的人”,“相似經歷”,“感覺興趣的校友”及“個人簡歷瀏覽地圖”。

我們重新考慮了前端的方法,加了客戶端模板到混合頁面(個人中心、我的大學頁面),這樣應用可以更加可互動,只要請求 JSON 或部分 JSON 資料。還有,模板頁面通過 CDN 和瀏覽器快取。我們也開始使用了 BigPipe 和 Play 框架,把我們的模型從執行緒化的伺服器變成非阻塞非同步的。

在程式碼之外,我們使用了 Apache 的多層代理和用 HAProxy 做負載均衡,資料中心,安全,智慧路由,服務端渲染,等等。

最後,我們繼續提升伺服器的效能,包含優化硬體,記憶體和系統的高階優化,利用新的 JRE。

下一步是什麼

LinkedIn 今天仍在快速增長,仍然有大量值得提升的工作要做,我們正在解決一些問題,看起來只解決了一部分 – 快來加入我們吧!

相關文章