https://yq.aliyun.com/articles/161190前端
摘要: 首屆阿里巴巴中間件技術峯會上,阿里巴巴中間件技術部專家唐三帶來「阿里電商架構演變之路」的演講,本文從阿里業務和技術架構開始引入,分別分享了阿里電商從1.0到4.0架構的演變之路,着重分析了分佈式和異地多活的改變之路。數據庫
首屆阿里巴巴中間件技術峯會上,阿里巴巴中間件技術部專家唐三帶來「阿里電商架構演變之路」的演講,本文從阿里業務和技術架構開始引入,分別分享了阿里電商從1.0到4.0架構的演變之路,着重分析了分佈式和異地多活的改變之路。後端
視頻回顧緩存
如下是精彩內容整理:服務器
阿里已經不僅僅有電商業務,今天咱們涉獵的很是普遍,佈局也很是多。阿里從一家電商公司開始,若是業務已經覆蓋到了各個行業,圖爲2015年的佈局。按照這樣的業務發展速度,若是沒有一套完整的技術體系支撐,勢必會影響整個業務的發展。markdown
能夠看到咱們的技術是分層的,在最上的是業務,中間部分是中間件、搜索和大數據等中臺系統。整個的大中臺體系就是中中間這層通用的技術可以快速支持上層業務的快速發展。只要是用發中臺的技術體系,都可以在上面快速的搭建本身的業務。架構
整個中臺包括部分如圖,除了中間件、搜索,還有一些數據分析。中間件在業務研發的過程當中起到很是重要的做用。這是在咱們整個技術架構演變過程當中,逐漸造成的一套體系。併發
淘寶從初創開始到今天,咱們的技術架構整體上經歷了四代:oracle
那麼接下來,我就從0開始,跟你們分享這一段歷程。框架
LAMP結構
整個淘寶網從開始想去建立,到真正上線,總共經歷了一個多月的時間。那這一個多月的時間都作了些什麼呢?
第一件事情,咱們開始作技術選型,決定咱們後續怎麼發展;第二件事情,如何在一個多月的時間,讓咱們的網站上線。
咱們購買了一套基於LAMP架構的電商網站,而且拿到源代碼,咱們對其進行二次開發,好比界面的UI改動,上下title的改動,其中最大的改動就是咱們對它的數據庫作了讀寫分離。
Java架構
隨着業務量的增加,就會發現一些瓶頸,主要來自於數據庫。當時的數據庫是MySQL4,還不夠穩定,數據庫常常會出現死機。所以,咱們直接把數據庫換成oracle,經過PHP和oracle直接去鏈接進行操做,但PHP不支持鏈接池,即便使用一些開源的PHP中間件,讓PHP去鏈接oracle,仍是很是不穩定,鏈接池的中間件常常卡死。
而後咱們開始考慮將技術體系轉成Java,由於Java在企業級的應用中,有着比較成熟的生態。轉化的過程當中也仍是很坎坷的:第一,咱們是一個線上正在運行的系統;第二,系統當時正在大規模的增加。因此說把系統替換成Java,最好的方法就是分塊替換。同時發現,oracle的寫入量仍是比較大的,當時還作了一個search,把產品搜索和店鋪搜索放到search裏面,這樣每一次的請求都打到數據庫裏面了,這樣咱們就完成了1.0架構到2.0架構的演進。
分佈式架構
隨着整個業務的發展,咱們又迎來了新問題,洪峯流量給咱們帶來了巨大的挑戰,電商行業在國內已經逐步開始盛行,咱們的流量直線上漲,電商的人口紅利也開始上漲。隨着流量的上漲,咱們面臨着服務器和數據庫的壓力。
從技術角度來看,咱們面臨着兩個問題:
首先是淘寶上描述商品的圖片特別多,圖片問題很是嚴重,主要取決於咱們採用的是商用存儲,成本很是高,最高級別版本也很難存儲下咱們全部的圖片;
其次在淘寶上瀏覽的全部交易都有交易快照,這也是很是耗費存儲成本的;
第三,雖然數據庫替換成了oracle,隨着數據量的增大,咱們全部的請求都打到數據庫上面,這時數據庫的存儲也快達到了極限,壓力也很是大。
因此咱們開始對架構升級。第一,咱們增長了內存cache,cache主要是解決數據庫壓力過大的問題,咱們本身研製了一套Key/Value 分佈式緩存(TAIR),就是在數據庫前端加了一個內存cache,緩解了咱們數據庫的壓力。第二,咱們增長了分佈式文件系統(TFS),以前的文件系統在商用的時候,成本過高,服務器的量很是大,因此咱們研發了本身的一套文件系統。
隨着咱們的業務量逐漸增大,人也愈來愈多,這就致使開發維護成本特別高,當時咱們是all-in-one系統,全部人改全部的代碼都是在這一個系統裏面,就會出現如下問題:
還有性能問題。隨着前端業務量的增大,服務器逐漸增多的時候,oracle也出現了鏈接數的瓶頸。因此咱們必須開始作新的架構,讓整個架構往前走一步。
因而,咱們邁向了3.0架構。系統進行拆分變小,拆分系統主要是把系統分層。把系統分紅三類:
第一類是c類,是中心類,好比說會員、商品、店鋪等等,基於這些中心上面開發各自的系統。好比說商品詳情、交易下單;
還有一些公共的類,是p類,好比交易平臺,這是從業務上進行拆分。幾個比較知名的項目,好比千島湖項目(拆分出交易中心、類目屬性中心)、五彩石項目(拆分出店鋪中心、商品中心、評價中心)。
伴隨着技術架構的改動,咱們的業務結構也開始進行改動,開始成立了相應的團隊。上圖的下半部分就介紹了咱們架構的演變過程。開始時,是all in one,1~10在維護一個項目。第二個階段是10~1000人維護的MVC架構,實現了先後端分離,各司其職。第三個階段是RPC,就是把各個系統進行拆分,而後各個系統之間進行通訊。第四個階段就是SOA這樣一個模式。
重點分享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上面。當咱們把分佈式系統變得龐大以後,其實各個系統各司己職就行了。
Oracle其實也產生了性能瓶頸,而MySQL通過了多年發展,已經很是的成熟和穩定了。咱們考慮把數據庫作拆分,也就是去IOE。對MySQL進行拆分,其實就是按照必定的規則去分庫分表。拆分首先就是讀寫分離,而後作垂直拆分,還有水平拆分。垂直拆分主要是按業務來拆分,當垂直拆分到必定程度以後,有一些大業務仍是不能承擔這樣的數據量,咱們只能水平作分庫分表,作sharding的拆分。分庫分表就基於某一些主鍵算,若是主鍵符合什麼樣的條件,就Sharding到什麼服務器上面。可是讓每個業務系統去作的成本是很是高的,必定要有一箇中間通用的東西,可以解決數據庫水平拆分的問題。
因此咱們開發了一套數據庫的中間件叫TDDL。TDDL就是在中間件層面支持數據庫的水平拆分,業務就是在寫單庫同樣,你不須要感知太多的東西,可是我已經把數據分散到各個數據庫裏面去了。當時還有一個系統是CORONA,CORONA今天咱們已經把他放到雲上面去了,它遵循標標準的JDBC協議,應用在寫代碼的時候仍是遵循標準的JDBC協議,徹底不須要感知任何的東西,用的也是標準的JDBC包。把請求送到咱們的server上面,server去作Sharding處理,整個對應用是徹底沒有感知的。
有了分庫分表,咱們如何把oracle的數據遷移到MySQL?對此,咱們開發了幾個中間件,第一個就是「愚公」,把oracle裏面的數據經過「愚公」一點一點地遷移到MySQL裏面去,放到各個庫裏面,同時保證咱們的業務不受影響;當咱們把數據庫作分庫分表以後,咱們還須要在數據庫和緩存之間作一些trigger,當數據變了,須要觸發一個事件,可能之前咱們須要經過寫一個程序來實現,如今咱們也沉澱了一套中間件系統——精衛,它會監聽每一個數據庫的變化,當數據庫每一個記錄發生變化以後,它就觸發一個事件,接聽到這個事件以後,業務方能夠根據本身的業務需求寫一個精衛的worker,而後放到精衛裏面去,觸發相應的邏輯。最典型的就是觸發cache邏輯。隨着咱們IDC架構以後,當咱們的數據在單點寫完以後,其餘的地域如何感知到數據的變化呢?就是經過精衛這樣一個系統實現的。當數據庫變化了,精衛會觸發失效cache,當業務請求再過來的時候,就會把cache裏面的數據填充成最新的數據,而後可以讓業務看到最新的數據,就不會出現當A單元數據變更了,而後B單元和C單元的cache沒有生效的狀況。
先是垂直拆分後是水平拆分,接下來就是對應用的拆分。應用拆分是經過HSF這個RPC框架解決應用之間的調用,解決同步調用,接下來還有異步調用。例如,我要建立一個訂單,訂單後面依賴了200多個系統,若是按照同步調用一步步進行下去,可能最終返回的返回時間會很是長,這時候怎麼辦呢?咱們會選擇併發,A去調用B、去調用C、去調用D的這種HSF直接調用。但這時存在一個問題,若是說下游依賴的200多個系統中有一個系統被掛起了,就使整個請求被掛起了,而後接下來就很難進行了,並且這時若是對方的系統出現了嚴重的問題,會使我後續的請求都被掛起,最終也會把個人系統拖垮。這個時候咱們須要一個異步解耦的方式,那麼就產生了消息中間件。消息中間件就是當A要去調用的時候,而後就會發一個消息,而後下游的系統開始訂閱這條消息,各自去處理各自的,處理完以後把結果返回給中間件,這樣就完成了異步通訊過程。那麼若是其中某一個系統發生了問題,前端的交易系統建立訂單的時候,它只要把消息發出去就不用管了,等全部的事情都處理完再回調它就能夠了,就不會關注你如何調用。
圖爲分佈式消息的處理過程。在內部咱們主要用的消息是NOTIFY/METAQ。如今咱們整體上都會在一個消息中間件上合併,其實並不須要這麼多的中間件。應用場景就是分佈式最終一致性、應用解耦、異步、並行等一系列問題。從整個物理部署也能夠看出來,每一個都是集羣的,有name server、producer、consumer等,這又解決了一個穩定性問題。咱們單點沒有這個問題,隨便一個server掛掉,其實咱們整個仍是能夠通訊的,不會影響到業務的穩定性。
隨着咱們整個分佈式架構的演進,架構變得異常複雜,依賴關係也變得異常複雜。這時候咱們就想能不能可視化線上的問題,方便咱們知道究竟發生了什麼、它們之間的調用關係和調用鏈路是什麼樣。因而乎就產生了分佈式追蹤(EAGLEEYE)。
有了EAGLEEYE,咱們就能清楚地知道一個請求過來,是怎麼樣從入口一直傳遞到最後,中間都經歷了什麼,而後哪一塊多是有問題的。像圖中這樣報錯位置會標紅,咱們就能夠清晰的知道是哪一個系統出的問題。咱們不須要再像之前同樣,你們各在排查本身的系統,致使咱們處理問題的時間比較長。
異地多活
咱們整個架構演進到了4.0架構,其實到分佈式架構看似咱們已經解決掉了業務上的問題。可是,咱們會遇到新的問題,好比說資源問題、業務擴展性還有就是容災問題。資源中最重要的問題就是資源受限,當咱們的機房都在一個地方,這個地方並不能無限擴展,隨着咱們服務器數量愈來愈多,那麼這個地方可能就放不下咱們的服務器。好比2013年咱們買到機器以後,杭州的機房沒有地方去放。隨着咱們搞雙十一活動,伴隨着銷售額和秒級峯值都有很大的提高,咱們的成本也會有必定的提高,咱們最終也會遇到單地域資源的限制;第二個是擴展性,有些業務可能不能只在這一個地方部署,由於別人訪問我會比較慢,須要部署到國外,這時候業務有一個異地部署的需求;第三個就是一個容災的需求,畢竟天災人禍都在所不免。好比說一樣是在2013年,杭州是40度的高溫,咱們的機房差點被限電,還好最終沒有限。可是這也給了咱們一個警示,就是咱們必需要對咱們的架構進行演進。若是不演進的話,總有一天咱們的資源會不夠。
架構演進就是不把雞蛋放到同一個籃子裏面,咱們開始把咱們的業務劃分出各個邏輯的單元,能夠把它們放到各個地方,而後讓咱們整個系統分散到全球,各個系統之間也沒有過強的依賴,當某一個地域出現問題以後,不會影響到其餘地方,咱們只須要把流量切換一下就能夠。如今咱們應用的就是這套架構。
咱們按照業務的維度,把業務劃分紅各個邏輯單元。好比說第一個要作單元化的是交易單元,咱們就把整個交易鏈路劃分出來,放到各個邏輯單元裏面,而後在水平方向上進行拆分,而後把數據再在水平上作一個區分。單元內的數據就不要發跨單元。若是跨單元就會出現一些問題,好比說容災問題,若是A掛掉以後,可能不止影響到A,其餘單元也會受到影響。若是發生跨單元調用,延時也會比較長,對於最終用戶下單的體驗也是很是差的。因此咱們要遵循單元封閉的邏輯,讓每個單元內的調用關係都封閉在本身的單元內,不要發生跨單元。
在技術架構上,咱們對技術作了一個分層,而且定了幾個原則,除了單元封閉原則以外,還有全局路由,全局路由解決的是全局用戶流量的分配,當一個用戶流量進來以後,它會按照咱們的路由規則分配到相應的單元裏面去。當用戶流量進來,會跳到CDN,CDN知道其屬於哪一個單元。而後到了某一單元以後,接入層會判斷其是否屬於這個單元,若是不屬於,會讓其跳到正確的單元裏面去。若是屬於這個單元就繼續往下走,直到走到數據庫這一層。若是數據庫這一層出現了問題,咱們作的是數據庫寫失敗,也不可以讓其寫成功,因此數據要一致。這時候對於數據一致又出現了新的問題,好比說咱們按照買家訂單寫到各個單元裏面,可是賣家如何發貨呢?賣家實際上是放到各個中內心面的,買家的全部下單數據都要同步到賣家這裏。咱們的模式就是最終一致性,就是對時間不是很敏感,我能夠慢慢的同步,好比說,買家下了單,過了一兩秒以後才能同步到賣家那裏,其實這個時間是你們都可以接受的。對於強一致類型的數據,咱們就跨單元,在同一個地點去撿數據。就是圖上面紅色部分——強中心依賴,因此這是咱們交易鏈中核心——跨單元依賴。
咱們整個單元化的項目經歷了三年。2013年咱們開始在杭州作了兩個POC驗證,驗證一下同城按照這種邏輯單元隔離開來,看看可否調用成功。2014年咱們在杭州、上海近距離的兩個城市之間作了異地多活的嘗試。2015年咱們開始在千里以外的地域去部署三地四單元架構,其中一個單元是雲單元,這是咱們爲了下降成本,咱們開始使用雲機器來搞咱們雙十一的大促。到2016年、2017年咱們的單元數愈來愈多,分佈的愈來愈廣,每個單元均可以作一些相應的嘗試。
這就是咱們整個異地多活的架構,異地多活解決的就是容災問題、資源問題還有業務的擴展性問題。只要咱們發現資源不夠了,咱們只須要建立一個新的單元,就能夠把容量擴上去。首先調用就把資源統一了,建站平臺經過調用就能夠快速搭建一個單元。當這個單元經過全鏈路壓測以後,咱們整個單元就能夠通入使用,這樣容量的問題就獲得瞭解決。那麼容災的問題經過全局路由就能夠解決。當某個單元出了問題以後,咱們只要快速的把流量切換出去就能夠。業務擴展性是整個架構自然具有的,咱們已經在千里以外把這個單元部署過了。
高可用問題也是咱們面臨的一個比較大的問題,在2013年之前雙十一前幾分鐘的成功率是很低的,不少人是沒法購物的。可是在2013年以後,經過全鏈路壓側這樣一個技術,可以提早模擬雙十一零點這一刻的洪峯流量,使得咱們可以提早把問題解決掉,因此說整個購物體驗愈來愈順滑。高可用在整個雙十一備戰過程當中起到一個很是核心的做用。
當咱們的業務在分佈式和異步化以後,並且流量猛烈上漲以後,咱們遇到的最大問題是容量評估:就是我也不知道我須要準備多少機器來抗這些流量,我也不知道我上下游依賴的應用應該準備多少流量,由於我根本不太清楚咱們之間詳細的調用是什麼樣子的。用戶來的時候不一樣的調用鏈路可能調用彼此的次數不同,因此說容量是很難評估的。因此咱們首先模擬雙十一零點這一時刻的流量,把這個流量製造出來,看一下場景。經過咱們製造的數據,提早把咱們雙十一的問題暴漏出來。
高可用體系自己就是一套體系,它們之間彼此依賴,是閉環的。好比說咱們一個單元的容量只有十萬,當我容量超過十萬的時候該怎麼辦呢?其實在雙十一的時候,若是數據超過十萬,系統會出現一個頁面告訴你正在排隊——限流。限流是怎麼產生的呢?其實就是爲了使咱們可以更好的服務於咱們可以服務的用戶範圍。對一個業務設定了一個閾值以後,當流量超過了閾值,就開始進行限流。這個時候若是我想提高本身的彈性,應該把那些沒有達到的閾值、也就是水位比較低的應用,把它的機器彈過來,彈到水位比較高的應用。
除了這些以外,其實咱們的高可用還有不少,好比說關於容量能力,我剛纔提到了壓測,全鏈路和單鏈路,還有線上的單機壓測,容量評估。靜態架構有灰度發佈、Eagleeye跟蹤。運行態有xflush/alimonitor、業務防止損BCP/DCP/Holo、限流降級Sentinel、開關平臺Switch、預案系統Preplan、流量調度Failover等,還有應用資源管理應用彈性伸縮Athena、資源調度Zeus、運行環境隔離Moses等。這樣咱們高可用體系就造成一個閉環。其中一個場景就是,好比咱們進行壓測,這邊會限流,這該怎麼辦呢?這時候彈性開始往外彈,把整個水位調勻,這樣會使咱們經過壓測。第二個場景就是當我一個應用掛了以後,我把流量切到另一個地方去了,就去觸發限流,若是這時候咱們還有資源,咱們應該利用彈性,把水位彈上來。
新起點
雲會變成如同水電煤同樣的基礎資源,愈來愈多的業務會在雲上展示,這些業務中會有不少經歷如同淘寶同樣的發展,咱們將加速這些業務的發展進程,創造更大價值,用技術驅動業務,把咱們的技術能力輸出到雲上去。
如今咱們不僅服務於咱們的雙十一,咱們也想爲其它企業提供技術服務,用技術驅動他們,讓他們也能只關心業務就好,不用去過多的關心底層是如何實現的。上面是一些在雲端的產品,在阿里雲上能夠直接看到,像DRDS、EDAS、MQ等。
如今整理阿里的架構叫Aliware。Aliware已經在雲上爲企業提供企業級的互聯網架構,支持了不少的企業。