1. 程式人生 > >阿里電商架構演變之路

阿里電商架構演變之路

轉自:雲棲https://yq.aliyun.com/articles/161190

阿里已經不單單有電商業務,今天我們涉獵的非常廣泛,佈局也非常多。阿里從一家電商公司開始,如果業務已經覆蓋到了各個行業,圖為2015年的佈局。按照這樣的業務發展速度,如果沒有一套完整的技術體系支撐,勢必會影響整個業務的發展。

3373156d2955b94e37bab49d82a9d86320f75e5d

可以看到我們的技術是分層的,在最上的是業務,中間部分是中介軟體、搜尋和大資料等中臺系統。整個的大中臺體系就是中中間這層通用的技術能夠快速支援上層業務的快速發展。只要是用發中臺的技術體系,都能夠在上面快速的搭建自己的業務。

0686de17ba73768345331b528914112101f61f21

整個中臺包括部分如圖,除了中介軟體、搜尋,還有一些資料分析。中介軟體在業務研發的過程中起到非常重要的作用。這是在我們整個技術架構演變過程中,逐漸形成的一套體系。

淘寶從初創開始到今天,我們的技術架構總體上經歷了四代:

  • 第一代是基於LAMP的一套結構。
  • 第二代是基於Java的應用架構。
  • 第三代是基於分散式體系,構建出一整套的分散式架構。
  • 第四代是基於IDC,不但應用能夠分佈,整數資料中心也能夠分佈。

那麼接下來,我就從0開始,跟大家分享這一段歷程。

LAMP結構

整個淘寶網從開始想去建立,到真正上線,總共經歷了一個多月的時間。那這一個多月的時間都做了些什麼呢?

66281ddd5d7ada9f107df93cb17110776f26c7da

第一件事情,我們開始做技術選型,決定我們後續怎麼發展;第二件事情,如何在一個多月的時間,讓我們的網站上線。

我們購買了一套基於LAMP架構的電商網站,並且拿到原始碼,我們對其進行二次開發,比如介面的UI改動,上下title的改動,其中最大的改動就是我們對它的資料庫做了讀寫分離。

Java架構

af3ede6d612024ed0ebc4d4000e7eb2f71953bad

隨著業務量的增長,就會發現一些瓶頸,主要來自於資料庫。當時的資料庫是MySQL4,還不夠穩定,資料庫經常會出現宕機。因此,我們直接把資料庫換成oracle,通過PHP和oracle直接去連線進行操作,但PHP不支援連線池,即使使用一些開源的PHP中介軟體,讓PHP去連線oracle,還是非常不穩定,連線池的中介軟體經常卡死。

然後我們開始考慮將技術體系轉成Java,因為Java在企業級的應用中,有著比較成熟的生態。轉化的過程中也還是很坎坷的:第一,我們是一個線上正在執行的系統;第二,系統當時正在大規模的增長。所以說把系統替換成Java,最好的方法就是分塊替換。同時發現,oracle的寫入量還是比較大的,當時還做了一個search,把產品搜尋和店鋪搜尋放到search裡面,這樣每一次的請求都打到資料庫裡面了,這樣我們就完成了1.0架構到2.0架構的演進。

分散式架構

隨著整個業務的發展,我們又迎來了新問題,洪峰流量給我們帶來了巨大的挑戰,電商行業在國內已經逐步開始盛行,我們的流量直線上漲,電商的人口紅利也開始上漲。隨著流量的上漲,我們面臨著伺服器和資料庫的壓力。

從技術角度來看,我們面臨著兩個問題:

首先是淘寶上描述商品的圖片特別多,圖片問題非常嚴重,主要取決於我們採用的是商用儲存,成本非常高,最高級別版本也很難儲存下我們所有的圖片;

其次在淘寶上瀏覽的所有交易都有交易快照,這也是非常耗費儲存成本的;

第三,雖然資料庫替換成了oracle,隨著資料量的增大,我們所有的請求都打到資料庫上面,這時資料庫的儲存也快達到了極限,壓力也非常大。

6e0c07a6ff277801b9211e60b02ad28c1003a141

所以我們開始對架構升級。第一,我們增加了記憶體cache,cache主要是解決資料庫壓力過大的問題,我們自己研製了一套Key/Value 分散式快取(TAIR),就是在資料庫前端加了一個記憶體cache,緩解了我們資料庫的壓力。第二,我們增加了分散式檔案系統(TFS),之前的檔案系統在商用的時候,成本太高,伺服器的量非常大,所以我們研發了自己的一套檔案系統。

a56b7f8802541c4070e8b1a70ca2dcdb42d82e30

隨著我們的業務量逐漸增大,人也越來越多,這就導致開發維護成本特別高,當時我們是all-in-one系統,所有人改所有的程式碼都是在這一個系統裡面,就會出現以下問題:

  • 技術團隊規模500人左右,維護變得越來越複雜
  • 單一War應用,應用包一直增長,更新業務特性越來越慢;資料逐步形成多個孤島,無法拉通
  • 基於傳統應用開發架構,業務爆發,彈性不足,單點故障影響巨大

還有效能問題。隨著前端業務量的增大,伺服器逐漸增多的時候,oracle也出現了連線數的瓶頸。所以我們必須開始做新的架構,讓整個架構往前走一步。

d5e76b954d51db3b112ee5561a0fd34ff9dcec9c

於是,我們邁向了3.0架構。系統進行拆分變小,拆分系統主要是把系統分層。把系統分成三類:

第一類是c類,是中心類,比如說會員、商品、店鋪等等,基於這些中心上面開發各自的系統。比如說商品詳情、交易下單;

還有一些公共的類,是p類,比如交易平臺,這是從業務上進行拆分。幾個比較知名的專案,比如千島湖專案(拆分出交易中心、類目屬性中心)、五彩石專案(拆分出店鋪中心、商品中心、評價中心)。

伴隨著技術架構的改動,我們的業務結構也開始進行改動,開始成立了相應的團隊。上圖的下半部分就介紹了我們架構的演變過程。開始時,是all in one,1~10在維護一個專案。第二個階段是10~1000人維護的MVC架構,實現了前後端分離,各司其職。第三個階段是RPC,就是把各個系統進行拆分,然後各個系統之間進行通訊。第四個階段就是SOA這樣一個模式。

e43e2c9fd261b2c74def156c2a3070f3e6a81d49

重點分享RPC的發展歷程。我們把應用拆成才c類、p類、垂直團隊類。這些應用本來就是在一個大應用裡面,他們之間可以很自然地進行通訊,可以直接進行相互呼叫。但是拆分之後,我們的應用如何進行相互呼叫?

我們開發了輕量級的HSF框架,它是基於Java interface的RPC框架,使得開發系統時就像開發本地應用一樣去正常的呼叫Java。使用這個框架可以真正遠端的呼叫到其他的系統上面。

隨著應用逐漸發展,我們會依賴中介軟體或者各個產品之間相互依賴。為了解決Jar包衝突的問題,我們研究出了Pandora容器。這個容器能把所有的加包做隔離,當發生Jar包衝突的時候,它知道優先載入哪個,這樣,我們就把Jar包衝突的問題解決了,那服務發現怎麼辦呢?比如:一個應用A如何知道應用B裡面有多少臺機器呢,你的ip又是什麼?最簡單的方式就是靜態列表,記錄下ip,做輪詢策略,先呼叫A的1號機,再呼叫2號機。這樣就沒法實現一個動態的發現。所以在這個過程中,我們有一個動態的配置中心(configserver),在你服務上線的時候,作為provider把服務放到configserver上去,當需要消費這個服務的時候,會看哪些服務可以呼叫,然後把這個列表拿到。Configserver會自動把相應的provider的ip推送到consumer上面。然後consumer會自動發現provider的服務,然後彼此會發生一個相互呼叫關係。如果configserver掛掉之後,你的provider是掛不上去的,但是已經發上去的服務是不受影響的,因為configserver已經把相應的服務推送到consumer上面。當我們把分散式系統變得龐大之後,其實各個系統各司己職就好了。

be90a65f030b75d72628620cd6b746c2e5eb1ce5

Oracle其實也產生了效能瓶頸,而MySQL經過了多年發展,已經非常的成熟和穩定了。我們考慮把資料庫做拆分,也就是去IOE。對MySQL進行拆分,其實就是按照一定的規則去分庫分表。拆分首先就是讀寫分離,然後做垂直拆分,還有水平拆分。垂直拆分主要是按業務來拆分,當垂直拆分到一定程度之後,有一些大業務還是不能承擔這樣的資料量,我們只能水平做分庫分表,做sharding的拆分。分庫分表就基於某一些主鍵算,如果主鍵符合什麼樣的條件,就Sharding到什麼伺服器上面。但是讓每一個業務系統去做的成本是非常高的,一定要有一箇中間通用的東西,能夠解決資料庫水平拆分的問題。

50ef65ec73c8098583357a54cb022daf410319c3

所以我們開發了一套資料庫的中介軟體叫TDDL。TDDL就是在中介軟體層面支援資料庫的水平拆分,業務就是在寫單庫一樣,你不需要感知太多的東西,但是我已經把資料分散到各個資料庫裡面去了。當時還有一個系統是CORONA,CORONA今天我們已經把他放到雲上面去了,它遵循標標準的JDBC協議,應用在寫程式碼的時候還是遵循標準的JDBC協議,完全不需要感知任何的東西,用的也是標準的JDBC包。把請求送到我們的server上面,server去做Sharding處理,整個對應用是完全沒有感知的。

有了分庫分表,我們如何把oracle的資料遷移到MySQL?對此,我們開發了幾個中介軟體,第一個就是“愚公”,把oracle裡面的資料通過“愚公”一點一點地遷移到MySQL裡面去,放到各個庫裡面,同時保證我們的業務不受影響;當我們把資料庫做分庫分表之後,我們還需要在資料庫和快取之間做一些trigger,當資料變了,需要觸發一個事件,可能以前我們需要通過寫一個程式來實現,現在我們也沉澱了一套中介軟體系統——精衛,它會監聽每個資料庫的變化,當資料庫每個記錄發生變化之後,它就觸發一個事件,接聽到這個事件之後,業務方可以根據自己的業務需求寫一個精衛的worker,然後放到精衛裡面去,觸發相應的邏輯。最典型的就是觸發cache邏輯。隨著我們IDC架構之後,當我們的資料在單點寫完之後,其他的地域如何感知到資料的變化呢?就是通過精衛這樣一個系統實現的。當資料庫變化了,精衛會觸發失效cache,當業務請求再過來的時候,就會把cache裡面的資料填充成最新的資料,然後能夠讓業務看到最新的資料,就不會出現當A單元資料變動了,然後B單元和C單元的cache沒有生效的情況。

9f3511d1a1c96beedfae16481609a7889957b041

先是垂直拆分後是水平拆分,接下來就是對應用的拆分。應用拆分是通過HSF這個RPC框架解決應用之間的呼叫,解決同步呼叫,接下來還有非同步呼叫。例如,我要建立一個訂單,訂單後面依賴了200多個系統,如果按照同步呼叫一步步進行下去,可能最終返回的返回時間會非常長,這時候怎麼辦呢?我們會選擇併發,A去呼叫B、去呼叫C、去呼叫D的這種HSF直接呼叫。但這時存在一個問題,如果說下游依賴的200多個系統中有一個系統被掛起了,就使整個請求被掛起了,然後接下來就很難進行了,而且這時如果對方的系統出現了嚴重的問題,會使我後續的請求都被掛起,最終也會把我的系統拖垮。這個時候我們需要一個非同步解耦的方式,那麼就產生了訊息中介軟體。訊息中介軟體就是當A要去呼叫的時候,然後就會發一個訊息,然後下游的系統開始訂閱這條訊息,各自去處理各自的,處理完之後把結果返回給中介軟體,這樣就完成了非同步通訊過程。那麼如果其中某一個系統發生了問題,前端的交易系統建立訂單的時候,它只要把訊息發出去就不用管了,等所有的事情都處理完再回調它就可以了,就不會關注你如何呼叫。 

8bb8c02bffc1be25cecc4bf5aa88c5123882bf46

圖為分散式訊息的處理過程。在內部我們主要用的訊息是NOTIFY/METAQ。現在我們總體上都會在一個訊息中介軟體上合併,其實並不需要這麼多的中介軟體。應用場景就是分散式最終一致性、應用解耦、非同步、並行等一系列問題。從整個物理部署也可以看出來,每個都是叢集的,有name server、producer、consumer等,這又解決了一個穩定性問題。我們單點沒有這個問題,隨便一個server掛掉,其實我們整個還是可以通訊的,不會影響到業務的穩定性。

隨著我們整個分散式架構的演進,架構變得異常複雜,依賴關係也變得異常複雜。這時候我們就想能不能視覺化線上的問題,方便我們知道究竟發生了什麼、它們之間的呼叫關係和呼叫鏈路是什麼樣。於是乎就產生了分散式追蹤(EAGLEEYE)。

b7e601b032a7013cf786d39096afc4d080fdcacb

有了EAGLEEYE,我們就能清楚地知道一個請求過來,是怎麼樣從入口一直傳遞到最後,中間都經歷了什麼,然後哪一塊可能是有問題的。像圖中這樣報錯位置會標紅,我們就可以清晰的知道是哪個系統出的問題。我們不需要再像以前一樣,大家各在排查自己的系統,導致我們處理問題的時間比較長。

異地多活

e8eded8619c79cc89f05753ce2e45c2a7e42b16e

我們整個架構演進到了4.0架構,其實到分散式交媾看似我們已經解決掉了業務上的問題。但是,我們會遇到新的問題,比如說資源問題、業務擴充套件性還有就是容災問題。資源中最重要的問題就是資源受限,當我們的機房都在一個地方,這個地方並不能無限擴充套件,隨著我們伺服器數量越來越多,那麼這個地方可能就放不下我們的伺服器。比如2013年我們買到機器之後,杭州的機房沒有地方去放。隨著我們搞雙十一活動,伴隨著銷售額和秒級峰值都有很大的提升,我們的成本也會有一定的提升,我們最終也會遇到單地域資源的限制;第二個是擴充套件性,有些業務可能不能只在這一個地方部署,因為別人訪問我會比較慢,需要部署到國外,這時候業務有一個異地部署的需求;第三個就是一個容災的需求,畢竟天災人禍都在所難免。比如說同樣是在2013年,杭州是40度的高溫,我們的機房差點被限電,還好最終沒有限。但是這也給了我們一個警示,就是我們必須要對我們的架構進行演進。如果不演進的話,總有一天我們的資源會不夠。

fe348e472cdeac7cbdf405872bebc524b9111001

架構演進就是不把雞蛋放到同一個籃子裡面,我們開始把我們的業務劃分出各個邏輯的單元,可以把它們放到各個地方,然後讓我們整個系統分散到全球,各個系統之間也沒有過強的依賴,當某一個地域出現問題之後,不會影響到其他地方,我們只需要把流量切換一下就可以。現在我們應用的就是這套架構。

我們按照業務的維度,把業務劃分成各個邏輯單元。比如說第一個要做單元化的是交易單元,我們就把整個交易鏈路劃分出來,放到各個邏輯單元裡面,然後在水平方向上進行拆分,然後把資料再在水平上做一個區分。單元內的資料就不要發跨單元。如果跨單元就會出現一些問題,比如說容災問題,如果A掛掉之後,可能不止影響到A,其他單元也會受到影響。如果發生跨單元呼叫,延時也會比較長,對於終端使用者下單的體驗也是非常差的。所以我們要遵循單元封閉的邏輯,讓每一個單元內的呼叫關係都封閉在自己的單元內,不要發生跨單元。

0bf4d7119cf9ec4f0d4aabd7a7781cec709ed00d

在技術架構上,我們對技術做了一個分層,並且定了幾個原則,除了單元封閉原則之外,還有全域性路由,全域性路由解決的是全域性使用者流量的分配,當一個使用者流量進來之後,它會按照我們的路由規則分配到相應的單元裡面去。當用戶流量進來,會跳到CDN,CDN知道其屬於哪個單元。然後到了某一單元之後,接入層會判斷其是否屬於這個單元,如果不屬於,會讓其跳到正確的單元裡面去。如果屬於這個單元就繼續往下走,直到走到資料庫這一層。如果資料庫這一層出現了問題,我們做的是資料庫寫失敗,也不能夠讓其寫成功,所以資料要一致。這時候對於資料一致又出現了新的問題,比如說我們按照買家訂單寫到各個單元裡面,但是賣家如何發貨呢?賣家其實是放到各個中心裡面的,買家的所有下單資料都要同步到賣家這裡。我們的模式就是最終一致性,就是對時間不是很敏感,我可以慢慢的同步,比如說,買家下了單,過了一兩秒之後才能同步到賣家那裡,其實這個時間是大家都能夠接受的。對於強一致型別的資料,我們就跨單元,在同一個地點去撿資料。就是圖上面紅色部分——強中心依賴,所以這是我們交易鏈中核心——跨單元依賴。

我們整個單元化的專案經歷了三年。2013年我們開始在杭州做了兩個POC驗證,驗證一下同城按照這種邏輯單元隔離開來,看看能否呼叫成功。2014年我們在杭州、上海近距離的兩個城市之間做了異地多活的嘗試。2015年我們開始在千里之外的地域去部署三地四單元架構,其中一個單元是雲單元,這是我們為了降低成本,我們開始使用雲機器來搞我們雙十一的大促。到2016年、2017年我們的單元數越來越多,分佈的越來越廣,每一個單元都可以做一些相應的嘗試。

207c871dd8a9f4830dfe346f6308b469f9324524

這就是我們整個異地多活的架構,異地多活解決的就是容災問題、資源問題還有業務的擴充套件性問題。只要我們發現資源不夠了,我們只需要建立一個新的單元,就可以把容量擴上去。首先呼叫就把資源統一了,建站平臺通過呼叫就可以快速搭建一個單元。當這個單元通過全鏈路壓測之後,我們整個單元就可以通入使用,這樣容量的問題就得到了解決。那麼容災的問題通過全域性路由就可以解決。當某個單元出了問題之後,我們只要快速的把流量切換出去就可以。業務擴充套件性是整個架構天然具備的,我們已經在千里之外把這個單元部署過了。

高可用問題也是我們面臨的一個比較大的問題,在2013年以前雙十一前幾分鐘的成功率是很低的,很多人是無法購物的。但是在2013年之後,通過全鏈路壓側這樣一個技術,能夠提前模擬雙十一零點這一刻的洪峰流量,使得我們能夠提前把問題解決掉,所以說整個購物體驗越來越順滑。高可用在整個雙十一備戰過程中起到一個非常核心的作用。

39c7e68fa5849725ad185daf9794076237f5f8c8

當我們的業務在分散式和非同步化之後,而且流量猛烈上漲之後,我們遇到的最大問題是容量評估:就是我也不知道我需要準備多少機器來抗這些流量,我也不知道我上下游依賴的應用應該準備多少流量,因為我根本不太清楚我們之間詳細的呼叫是什麼樣子的。使用者來的時候不同的呼叫鏈路可能呼叫彼此的次數不一樣,所以說容量是很難評估的。所以我們首先模擬雙十一零點這一時刻的流量,把這個流量製造出來,看一下場景。通過我們製造的資料,提前把我們雙十一的問題暴漏出來。

10bff93acec0cab183a92c0e4f59cb110528f431

高可用體系本身就是一套體系,它們之間彼此依賴,是閉環的。比如說我們一個單元的容量只有十萬,當我容量超過十萬的時候該怎麼辦呢?其實在雙十一的時候,如果資料超過十萬,系統會出現一個頁面告訴你正在排隊——限流。限流是怎麼產生的呢?其實就是為了使我們能夠更好的服務於我們能夠服務的使用者範圍。對一個業務設定了一個閾值之後,當流量超過了閾值,就開始進行限流。這個時候如果我想提升自己的彈性,應該把那些沒有達到的閾值、也就是水位比較低的應用,把它的機器彈過來,彈到水位比較高的應用。

除了這些之外,其實我們的高可用還有很多,比如說關於容量能力,我剛才提到了壓測,全鏈路和單鏈路,還有線上的單機壓測,容量評估。靜態架構有灰度釋出、Eagleeye跟蹤。執行態有xflush/alimonitor、業務防止損BCP/DCP/Holo、限流降級Sentinel、開關平臺Switch、預案系統Preplan、流量排程Failover等,還有應用資源管理應用彈性伸縮Athena、資源排程Zeus、執行環境隔離Moses等。這樣我們高可用體系就形成一個閉環。其中一個場景就是,比如我們進行壓測,這邊會限流,這該怎麼辦呢?這時候彈性開始往外彈,把整個水位調勻,這樣會使我們通過壓測。第二個場景就是當我一個應用掛了之後,我把流量切到另外一個地方去了,就去觸發限流,如果這時候我們還有資源,我們應該利用彈性,把水位彈上來。

新起點

雲會變成如同水電煤一樣的基礎資源,越來越多的業務會在雲上展現,這些業務中會有很多經歷如同淘寶一樣的發展,我們將加速這些業務的發展程序,創造更大價值,用技術驅動業務,把我們的技術能力輸出到雲上去。

efad81d4190e5dcf9bdbfa9dacb02d5543b9d617

現在我們不只服務於我們的雙十一,我們也想為其它企業提供技術服務,用技術驅動他們,讓他們也能只關心業務就好,不用去過多的關心底層是如何實現的。上面是一些在雲端的產品,在阿里雲上可以直接看到,像DRDS、EDAS、MQ等。

14166198f39bb177bdb8b5eaf9097dda053222c0

現在整理阿里的架構叫Aliware。Aliware已經在雲上為企業提供企業級的網際網路架構,支援了很多的企業。