阿里電商架構演變之路

轉自:雲棲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已經在雲上爲企業提供企業級的互聯網架構,支持了很多的企業。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章