1. 程式人生 > >《淘寶技術這十年》讀書筆記: 分散式時代和中介軟體

《淘寶技術這十年》讀書筆記: 分散式時代和中介軟體

這篇文章主要講述分散式時代和中介軟體相關知識,包括服務化、HSF、Notify和TDDL。同時裡面有我們經常遇見的編碼錯誤等相關問題,希望文章對你有所幫助!

一. 分散式時代

在系統發展的過程中,架構師的眼光至關重要,作為程式設計師,只要把功能實現即可,但作為架構師,要考慮系統的擴充套件性、重用性,對於這種敏銳的感覺,有人說是一種“程式碼潔癖”。淘寶早期有幾個架構師就具備了這種感覺,周銳虹開發的Webx是一個擴充套件性很強的框架,行癲在這個框架上插入了資料分庫路由的模組、Session框架等。在做淘寶後臺系統時,同樣需要這幾個模組,行癲指導我把這些模組單獨打成JAR包。

上面說的都是比較小的複用模組,到2006年,我們做了一個商品類目屬性的改造,在類目中引入了屬性的概念。專案代號叫“泰山”,這是一個舉足輕重的專案,這個改變是一個劃時代的創新。

在這之前三年時間內,商品的分類都是按照樹狀一級一級的節點來分的,隨著商品數量增長,類目也變得越來越深、複雜。這樣,買家如果查詢一件商品,就要逐級開啟類目,找商品之前要弄清商品的分類。一個很嚴重的問題,例如男裝裡有T恤、T恤下面有耐克、耐克有純棉的,女裝也有T恤、T恤下面還是有耐克、耐克下有純棉,那是先分男女裝,再分款式、品牌和材質呢?還是先分品牌,再分款式、材質和男女裝呢?

這時一燈說品牌、款式、材質等都可以叫做“屬性”,屬性是類似Tag(標籤)的一個概念,與類目相比更加靈活,這樣也縮減了類目的深度。這個思想解決了分類的難題!

從系統角度來看,我們建立了“屬性”這樣一個數據結構,由於除了類目的子節點有屬性外,父節點也可能有屬性,於是類目屬性合起來也是一個結構化的資料物件。把它獨立出來作為一個服務,叫做Catserver(Category Server)。跟類目屬性密切關聯的商品搜尋功能獨立出來,叫做Hesper(金星)。Catserver和Hesper供淘寶的前後臺系統呼叫。

現在淘寶的商品類目屬性已經是全球最大的,幾乎沒有什麼類目的商品在淘寶上找不到(除違禁品),但最初的類目屬性改造完之後,缺乏屬性資料,尤其是數碼類。從哪裡弄這些資料呢?我們跟“中關村線上”合作,拿到了很多資料。

有了類目屬性給運營工作帶來了很大的便利,我們知道淘寶的運營主要就是類目的運營,什麼季節推出什麼商品,都要在類目屬性上做調整,讓買家容易找到。所屬商品的賣家要編輯一次自己的商品,如冬天把羽絨衣調整到女裝一級目錄下,但隨著商品量的增長,賣家的工作量越來越大。

到了2008年,我們研究了超市裡前後臺商品的分類,發現超市前後臺商品可以隨季節和關聯來調整擺放場景(例如著名的啤酒和尿布的關聯),後臺倉庫裡要按照自然類目來儲存,二者密切關聯,卻又相互分開。淘寶前臺展示的是根據運營需要擺放商品的類目和屬性。改造後的類目屬性服務取名為Forest(森林,與類目屬性有點神似。Catserver還用於提供賣家授權、品牌服務、關鍵詞等相關服務)。類目屬性的服務化是淘寶在系統服務化方面做的第一個探索。

2.一種常見的編碼錯誤

雖然個別架構師具備了“程式碼潔癖”,但淘寶前臺系統的業務量和程式碼量還是呈爆炸式的增長。

業務方總在後面催,開發人員不夠就繼續招人,招來的人根本看不懂原來的業務,只好摸索著在“合適的地方”加上一些“合適的程式碼”,看看執行起來像那麼回事後,就釋出上線。

在這樣的惡性迴圈中,系統越來越腫,業務的耦合性越來越高(高內聚、低耦合),開發的效率越來越低。借用當時較流行的一句話:“你寫一段程式碼,編譯一下能通過,半個小時過去了;編譯一下沒通過,半天就過去了。”在這種情況下,系統出錯的概率也逐步增長,這讓開發人員苦不堪言。感覺現在很多公司招實習生都是這種感覺。

2007年年底的時候,研發部空降了一位從矽谷來的高管——空聞大師。他是一位溫厚的長者,他告訴我們一切要以穩定為中心,所有影響系統穩定的因素都要解決掉。例如:每做一個日常修改,都必須對整個系統迴歸測試一遍;多個日常修改如果放在一個版本中,要是一個功能沒有測試通過,整個系統都不能釋出。我們把這個叫做“火車模型”,即任何一個乘客沒有上車,都不許發車。這樣做最直接的後果就是火車一直晚點,新功能上線更慢,我們能明顯感覺到業務放的不滿,壓力非常大。

現在回過頭來看,其實我們並沒有理解背後的思路。正是在這種要求下,我們不得不開始改變些東西,例如:把迴歸測試日常化,每天晚上都跑一遍整個系統的迴歸。

另外,在這種要求下,我們不得不對這個超級複雜的系統做肢解和重構,其中複用性最高的一個模組:使用者資訊模組開始拆分出來,我們叫它UIC(User Information Center)。在UIC中,它只處理最基礎的使用者資訊操作,例如getUserById、getUserByName等。

在另一方面,還有兩個新興的業務對系統基礎功能的拆分也提出了要求。在那時候,我們做了淘寶旅行(trip.taobao.com)和淘寶彩票(caipiao.taobao.com)兩個新業務,這兩個新業務在商品的展示和交易的流程上都跟主站的業務不一樣,機票是按照航班資訊展示的,彩票是按照雙色球、數字和足球的賽程來展示的。但用到的會員功能和交易功能是與主站差不多的,當時做起來很糾結,因為如果在主站中做,會有一大半跟主站無關的東西,如果重新做一個,會有很多重複建設。

最終我們決定不再給主站添亂了,就另起爐灶做了兩個新的業務系統,從查詢商品、購買商品、評價反饋、檢視訂單這一整個流程都重新寫了一套。現在在“我的淘寶”中檢視交易記錄,還能發現“已買到的寶貝”中把機票和彩票另外列出來了,他們沒加入到普通訂單中。

當時如果已經把會員、交易、商品、評價這些模組都拆分出來,就不用什麼都重做一遍了。

到2008年初,整個主動系統(有了機票、彩票系統之後,把原來的系統叫做主站)的容量已經達到了瓶頸,商品數在1億個以上,PV在2.5億個以上,會員數超過了5000萬個。這時Oracle的連線池數量都不夠用了,資料庫的容量到了極限,即使上層系統加機器也無法繼續擴容,我們只有把底層的基礎服務繼續拆分,從底層開始擴容,上層才能擴充套件,這才能容納未來三五年的增長。

於是我們啟動了一個更大的專案,即把交易這個核心業務模組拆分出來。

原來的淘寶交易除了跟商品管理耦合在一起,還在支付寶和淘寶之間轉換,跟支付寶耦合在一起,這會導致系統很複雜,使用者體驗也很不好。我們把交易的底層業務拆分出來,叫交易中心(TradeCenter,TC),所謂底層業務,就如建立訂單、減庫存、修改訂單狀態等原子型的操作;交易的上層業務叫交易管理(TradeManager,TM)例如拍下一件普通商品要對訂單、庫存、物流進行操作,拍下虛擬商品不需要對物流進行操作,這些在TM中完成。

3.業務模組化

類目屬性、使用者中心、交易中心,隨著這些模組逐步拆分和服務化改造,我們在系統架構方面也積累了不少經驗。到2008年年底就做了一個更大的專案,把淘寶所有的業務都模組化,這是繼2004年從LAMP架構到Java架構之間的第二次脫胎換骨。

我們對這個專案取了一個很霸氣的名字——“五彩石”(女媧煉石補天用的石頭)。這個系統重構的工作非常驚險,有人稱為“給一架高速飛行的飛機換髮動機”。他們把淘寶的系統拆分成了如下架構。

其中,UIC和Forest在上文已說過,TC、IC、SC分別是交易中心(Trade Center)、商品中心(Item Center)、店鋪中心(Shop Center),這些中心級別的服務只提供原子級的業務邏輯,如根據ID查詢商品、建立交易、減少庫存等操作。

再往上一次是業務系統TM(Trade Manager,交易業務)、IM(Item Manager,商品業務)、SM(Shop Manager,後來改名叫SS,即Shop System,店鋪業務)、Detail(商品詳情)。

拆分之後,系統之間的互動關係變得非常複雜。

系統這麼拆分的好處顯而易見,拆分之後每個系統可以單獨部署,業務簡單,方便擴容;有大量可重用的模組便於開發新的業務;能夠做到專人專事,讓技術人員更加專注於某一個領域。

這樣要解決的問題也很明顯,拆分後,系統之間還是必須要打交道的,越往底層的系統,呼叫它的客戶越多,這要求底層系統必須具有超大規模的容量和非常高的可用性。

另外,拆分之後的系統如何通訊?這裡需要兩種中介軟體系統,一種是實時呼叫的中介軟體(淘寶的HSF,高效能服務框架),一種是非同步訊息通知的中介軟體(淘寶的Notify)。另外,一個需要解決的問題是使用者在A系統登入後,到B系統的時候,使用者的登入資訊怎麼儲存?這又設計一個Session框架。再者,還有一個軟體工程方面的問題,這麼多層的一套系統,怎麼去測試它?

二. 中介軟體

1.HSF

其實網際網路網站發展過程類似於超市經營(此處省略超市銷售收銀的例子,可以想象下沃爾瑪排隊購物付款的場景吧),只是在技術層面用其他名詞來表達而已,例如:有叢集、分工、負載均衡、根據QoS分配資源等。

叢集:所有收銀員提供的都是收銀功能,每個收銀員都可以完成收款,可以認為所有的收銀員構成了一個叢集。網際網路叢集會受限於排程、資料庫、機房等。

分工:收銀員和打掃衛生的人分開,這種分工容易解決,而這種分工在網際網路中是一項重要而複雜的技術,涉及的主要有按功能和資料庫的不同拆分系統等。如何拆分和拆分後如何互動是需要面臨的兩個挑戰。隱藏會有高效能通訊框架、SOA平臺、訊息中介軟體、分散式資料層等基礎產品的誕生。

負載均衡:讓每個收銀臺排隊差不多長,設立小件通道、團購通道、VIP通道等,這些都可認為是叢集帶來的負載均衡的問題,從技術層面上實現自然比生活中複雜得多。

根據QoS(Quality of Service,服務質量)分配資源:部分員工僅在晚上加班的機制在生活中不難實現,但對網際網路應用而言,就是一件複雜而且極具挑戰的事。

而且生活中面對使用者增長的情況下,想出這些招應該不難。不過要掌握以上四點涉及的技術就相當複雜了,而且網際網路中涉及的其他很多技術還沒有在這個例子中展現出來。例如快取、CDN等優化手段;運轉狀況監測、功能降級、資源劣化、流控等可用性手段;自建機房、硬體組裝等成本控制手段。因此,構建一個網際網路網站確實是不容易的,技術含量十足,當然,經營一家超市也不簡單。

服務拆分之後,如何取得我需要的服務呢?

在“電視機”上,把每個叢集能提供的服務顯示出來。你不需要關心哪個人為你服務,當你有需要的時候,頭頂的電視機會告訴你哪個服務在哪個區域。當你去到這個區域時,系統會給你找到一個最快的服務通道。

這就是HSF(High-Speed Service Framework)的設計思想:

服務的提供者啟動時通過HSF框架向ConfigServer(類似超市的電視機)註冊服務資訊(介面、版本、超時時間、序列化方式等),ConfigServer上定義了所有可供呼叫的服務(同一個服務也可能有不同的版本);

服務呼叫者啟動時向ConfigServer註冊對哪些服務感興趣(介面、版本),當服務提供者的資訊變化時,ConfigServer向趕興趣的服務呼叫者推送新的服務資訊列表;呼叫者在呼叫時則根據服務資訊的列表直接訪問相應的服務提供者,無須經過ConfigServer。

我們注意ConfigServer並不會把服務提供者的IP地址推送給服務的呼叫者,HSF框架會根據負載狀況來選擇具體的伺服器,返回結果給呼叫者,這不僅統一了服務呼叫的方式,也實現了“軟負載均衡”。平時ConfigServer通過和服務提供者的心跳來感應服務提供者的存活狀態。

在HSF的支援下,服務叢集對呼叫者來說是“統一”的,服務之間是“隔離”的,這保證了服務的擴充套件性和應用的統一性。再加上HSF本身提供的“軟負載均衡”,服務層對應用層來說就是一片“私有云”了。

HSF框架以SAR包的方式部署到Jboss、Jetty或Tomcat下,在應用啟動時,HSF(High-Speed Service Framework,在開發團隊內部有一些人稱HSF為“好舒服”)服務隨機啟用。HSF旨在為淘寶的應用提供一個分散式的服務框架,HSF從分散式應用層面以及統一的釋出/呼叫方式層面為大家提供支援,更容易地開發分散式應用或使用公用功能模組,而不用考慮分散式領域中的各種細節技術,例如:遠端通訊、效能損耗、呼叫的透明化、同步非同步呼叫的問題。

HSF是一個分散式的標準Service方式的RPC(RemoteProcedure Call Protocol,遠端過程呼叫協議)框架。Service的定義基於OSGI的方式,通訊層採用TCP/IP協議。

HSF系統目前每天承擔300億次以上的服務呼叫,一些讀者可能會疑問:既然淘寶的服務化是漸進式的,那麼在HSF出現之前,系統之間的呼叫採用什麼方式呢?

這個有點“五花八門”。對於類目的呼叫方式是Forest打包成一個JAR包,在應用啟動時裝載到記憶體中,僅這個JAR包所佔用的記憶體就有800MB之多(因為淘寶的類目資料龐大),對於當時一般只有2GB記憶體的開發機來說,載入完類目資訊後,機器執行速度就非常慢。對於使用者資訊(UIC)來說,一開始呼叫方式是Hessian介面,還有一些系統是通過WebService、Socket甚至是HTTP請求來相互呼叫的。

每種呼叫方式都涉及各種超時、資訊的加解/密、引數的定義等問題,由此可見,在沒有HSF之前,系統之間的呼叫是錯綜複雜的。而隨著系統拆分得越來越多,必須由一個統一的中間層來處理這種問題,HSF就是在這種背景下誕生的。

2.Notify

HSF解決了服務呼叫的問題,我們再提出一個很早就說過的問題:使用者在銀行的閘道器付錢後,銀行需要通知到支付寶,但銀行的系統不一定能發出通知;如果通知發出了,不一定能通知到;如果通知到了,不一定不重複通知一遍。

這個狀況在支付寶持續了很長時間,非常痛苦。支付寶從淘寶剝離出來時,淘寶和支付寶之間的通訊也面臨同樣的問題,支付寶架構師魯肅提出用MQ(Message Queue)的方式來解決這個問題,我負責淘寶這邊讀取訊息的模組。但訊息數量上來後,常常造成擁堵,訊息的順序也會出錯,系統掛掉訊息也會掛掉。

然後魯肅提出做一個系統框架上的解決方案,把要發出的通知存到資料庫中,如果實時傳送失敗,再用一個時間程式來週期性地傳送這些通知,系統記錄下訊息中間狀態和時間戳,這樣就保證了訊息一定能發出,也一定能通知到,且通知帶有時間順序,甚至可以實現失去性的操作。

(PS:這個技術感覺以前做Android類似微信的隨手拍軟體時非常適用)

在“千島湖”專案和“五彩石”專案後,淘寶系統拆分成了很多個,他們之間也需要類似的通知。例如,拍下一件商品,在交易管理系統中完成時,它需要通知商品管理系統減少庫存,同時旺旺服務系統傳送旺旺提醒,通知物流系統上門取貨,通知SNS系統分享訂單,通知公安局的系統這是騙子等等。

使用者一次請求,在底層系統可能產生10次的訊息通知。這一大堆的通知資訊是非同步呼叫的(如果同步,系統耦合在一起就達不到拆分的目的),這些訊息通知需要一個強大的系統提供支援,從訊息的數量級上看,比支付寶和淘寶之間的訊息量又上了一個層次,於是按照類似的思路,一個更加強大的訊息中介軟體系統就誕生了,它的名字叫做Notify。

Notify是一個分散式的訊息中介軟體系統,支援訊息的訂閱、傳送和消費,其架構圖如下所示:

NotifyServer在ConfigServer上註冊訊息服務,訊息的客戶端通過ConfigServer訂閱訊息服務。某個客戶端呼叫NotifyServer傳送一條訊息,NotifyServer負責把訊息傳送到所有訂閱這個訊息的客戶端(參照HSF圖)。

為了保證訊息一定能發出,且對方一定能收到,訊息資料本身就需要記錄下來,這些資訊存放在資料庫中。由於訊息具有中間狀態(已傳送、未傳送等),應用系統通過Notify可以實現分散式事物——BASE(基本可用Basically Available、軟狀態Soft State、最終一致Eventually Consistent)。

NotifyServer可以水平擴充套件,NotifyClient也可以水平擴充套件,資料庫也可以水平擴充套件。從理論上講,這個訊息系統的吞吐量時沒有上限的,現在Notify系統每天承載了淘寶10億次以上的訊息通知。

下圖展示了建立一筆交易後,TC(交易中心)向Notify傳送一條訊息,後續Notify所完成的一系列訊息通知。

3.TDDL

有了HSF和Notify的支援,在應用級別中,整個淘寶網的系統可以拆分了,還有一個制約系統規模的更重要的因素就是資料庫,也必須拆分。

前面講過淘寶很早就對資料進行過分庫的處理,上層系統連線多個數據庫,中間有一個叫做DBRoute的路由來對資料進行統一訪問。DBRoute對資料進行多庫的操作、資料的整合,讓上層系統像操作一個數據庫一樣操作多個庫。隨著資料量的增長,對於庫表的分發有了更高的要求。例如,你的商品資料到了百億級別時,任何一個庫都無法存放了,於是分成2個、4個…1024個、2048個。分成這麼多,資料能存放了,那怎麼查詢它?

這時候,資料查詢的中介軟體就要能夠承擔這個重任了,它對上層來說,必須像查詢一個數據庫一樣來查詢資料,還要想查詢一個數據庫一樣快(每條查詢在幾毫秒內完成),TDDL就承擔了這樣一個工作。

另外,加上資料的備份、複製、主備切換等功能,這一套系統都在TDDL中完成。在外面有些系統也用DAL(資料訪問層)這個概念來命名這個中介軟體。TDDL實現了下面三個主要的特性:

1).資料訪問路由——將針對資料的讀寫請求傳送到最適合的地方

2).資料的多向非對稱複製——一次寫入,多點讀取

3).資料儲存的自由擴充套件——不再受限於單臺機器的容量瓶頸與速度瓶頸,平滑遷移

下圖展示了TDDL所處的位置:

在此我向大家推薦一個架構學習交流群。交流學習群號:897889510 裡面會分享一些資深架構師錄製的視訊錄影:有Spring,MyBatis,Netty原始碼分析,高併發、高效能、分散式、微服務架構的原理,JVM效能優化、分散式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多

大家逐漸發現,如果按照業務的發展規模和速度,那麼使用高階儲存和小型機的Oracle儲存的成本將難以控制,於是降低成本就成了必然。如何能夠在不影響業務正常發展的前提下,解決成本問題呢?

“對一部分資料庫使用MySQL”,DBA們的決策是這樣,於是分散式資料層的重擔就落到了華黎的頭上。當時的需求如下:對外統一一切資料訪問、支援快取和檔案儲存系統、能夠在Oracle和MySQL之間自由切換、支援搜尋引擎。

那麼,如何實現分散式Join(連線)?(跨節點後簡單Join就會變成M*N臺機器的合併,代價太大)如何實現高速多維度查詢?如何實現分散式事務?

於是動手我們自己做,名字叫Taobao Distributed Data Layer(TDDL,外號“頭都打了”),學習開源的Amoeba Proxy。這就是TDDL 1.0時代。

粗略統計下來,TDDL已經走過了4年時間,滿足了近700個業務應用的使用需求。其中有交易商品評價使用者等核心資料,也有不那麼有名的中小型應用。量變產生質變,如何能夠更好地幫助這些業務以更低的成本完成業務需求,將成為資料層未來最重要的挑戰。

最後希望文章對你有所幫助,如果文章有不足或錯誤的地方,還請海涵!文章寫到此處,感覺讀後感還是會應該以精簡為主,下次寫書籍讀後感儘量寫成一篇,而不是大量的摘抄原文。希望大家購買原書看看,非常不錯~