注:本文轉載自公眾號 infoQ,根據石佳寧在InfoQ舉辦的2016ArchSummit全球架構師(深圳)峰會上的演講整理而成。
老司機簡介
石佳寧,餓了么后臺支撐研發(fā)部負責人,目前任職于餓了么,現(xiàn)任平臺研發(fā)中心-后臺支撐部門負責人,主要負責餓了么外賣訂單、統(tǒng)一客服系統(tǒng)、BD銷售以及管理工 具、代理商管理平臺等系統(tǒng)的設計和研發(fā)工作。
先自我介紹一下,我于2014年加入餓了么,那時正是餓了么飛速發(fā)展的起始點。我一直從事后臺領域的研發(fā),比如BD系統(tǒng)、客服系統(tǒng)和訂單系統(tǒng),現(xiàn)在專注交易架構相關的工作。
今天要講的內容主要分為兩大部分。第一部分是在高速增長和愈加復雜的交易場景下,餓了么訂單的服務架構是如何演進的,究竟是什么支撐我們的發(fā)展。
快速增長下的業(yè)務場景
具體講之前,我先介紹一下我們的場景,因為脫離具體的場景所有架構演進沒有任何意義。上面這兩個圖表不是餓了么的數據,是第三方分析整個外賣市場的數據圖。左邊的圖表是從2011年開始,整個O2O市場以及外賣的份額逐年增加。2013年和2014年的時候發(fā)生了比較大的飛躍,餓了么也是在這個時間段訂單量開始猛增。右邊的圖表是用戶注重外賣平臺的因素分布。
從圖中可以看到,用戶很在意配送速度,在意交易的時效性。對于O2O或者餓了么訂單,交易的要求比傳統(tǒng)電商的高,因為交易一般一兩個小時就結束了。在2014年初,餓了么訂單量只有日均10萬單,到2014年底超過百萬,這是一個質的飛躍,10萬訂單的量級和百萬訂單的量級的要求非常不一樣。在2015年突破了日均300萬,到今年5月單日峰值突破500萬。
快速發(fā)展涉及很多問題。我們是一家創(chuàng)業(yè)公司,業(yè)務發(fā)展非???,可能準備不是很充分,比如說監(jiān)控、日志、告警、框架、消息、數據庫,很多基礎設施還在建設之中。在這個過程中出現(xiàn)一些問題是在所難免的,對系統(tǒng)的要求不是不能掛、不能出問題,而是出了問題要第一時間能恢復。這是整個系統(tǒng)架構的前提。
服務架構的演進
圖中所示是訂單的早期架構圖,比較簡單。這個架構在2014年的時候支撐了日均10萬的訂單,是一套很不錯的架構,依然在很多系統(tǒng)中完美運行。但是對于后來發(fā)展的場景,它已經曝露問題了,比如業(yè)務邏輯嚴重耦合、代碼管理很困難,因為數據庫都在一起,操作變更很難追溯。
更進一步的是,性能的瓶頸只能是靠服務器去硬抗,從物理架構到邏輯架構,都已經成為業(yè)務發(fā)展的掣肘了。于是,為了業(yè)務的發(fā)展,我們做了一些演進的工作。
演進工作的核心就是一個字“拆”,跟“拆”對等的就是分治的思想。怎么拆分呢?面向服務有很多拆分原則。我將拆分過程中最具幫助和指導性的點羅列了以下幾條。
第一是明確的定義。之前也確實犯了一些錯誤,為了拆而拆。其實我們需要更明確,什么才算是一個服務?服務一定具有非常獨立的技術能力或者業(yè)務能力,而且一定意義上能夠很抽象。
第二是松耦合。最基本的松耦合就是Customer的消費不依賴于Provider的某一個特定實現(xiàn),這樣服務器的內部變更不會影響外部消費,消費者可以切換到其他服務能力的提供方,這是最基本的松耦合。還有時間上的松耦合或者位置上的松耦合,我們希望的松耦合是消費方和服務方是可以分離的。
第三是基于領域的認知,這對于整個產品起到非常大的作用。因為當時整個餓了么所有系統(tǒng)是在一起的,基于領域的認知,在面向用戶的維度和面向商戶的維度做了切分,還有基于交易鏈路做了切分。
第四是單一職責和關注分離。簡單說,我們希望一個服務或者一個模塊擁有單一的能力,而不是承擔過多的職責,否則責任不清晰,導致能力也不清晰。
最后一點是可被驗證的結果。在訂單拆分的過程中我們犯了一些錯誤,當時認為這樣拆分是沒有問題的,但是過一、兩個月,并沒有帶來效率和能力的提升,反而是跨團隊的要求越來越多,能力要求也越來越多。這時候可能是拆錯了。如果是一個好的拆分一定有利于發(fā)展,拆分之后的發(fā)展是更迅速的。
基于這幾條原則,我們對餓了么的整體服務做拆分之后,如上圖所示,架構就有了一些變化,看起來跟剛才架構區(qū)別不大。把Order Service做了分離。當時拆分雖然比較垂直,但是用戶、商戶、金融、訂單等還是有一些橫向交互。
一個接口有一個非常明確的Owner,一個表、一個庫也能保證僅有單一的操作方,讓我感受比較直接的是,為服務的治理奠定了基礎,以后可以針對某項特定業(yè)務做一些降級、熔斷,以及單獨的監(jiān)控。拆分實際上是讓各自模塊的掌控力變得更強了,對業(yè)務起到更好的支撐作用。
這時每個部門或者每個團隊都負責自己獨立的領域,代碼和數據都拆分完畢是不是就可以了?但是后來發(fā)現(xiàn)好像還不對。因為雖然大的領域上確實已經干凈了,但是在小的領域上依然問題很多,訂單并不僅僅只有一張表,一個單一的模塊,其實還有很多復雜的內容。
在一些技術工作上,這些問題曝露得并不是那么明顯,那時候大家對于一些領域認知或者業(yè)務邊界的認識還是模糊的,沒有人界定這些。但是當更進一步地去發(fā)展一個領域的時候,還是會有職責不清晰或者能力模糊的地方。我們思考,還要基于業(yè)務進行更細膩的規(guī)劃。
于是我們把訂單本身做了一些業(yè)務層次的拆分,拆分之前首先要確認訂單到底在整個系統(tǒng)中,尤其是交易系統(tǒng)、O2O系統(tǒng)中承擔什么角色,擔負什么職責。
在這個思考過程中,我們的認知大概是以下四點。
第一,訂單是整個交易鏈路的核心,圍繞了一些相關服務,比如金額計算服務、催單服務、售中異常服務等,我們希望這些服務之間有明確的區(qū)別。
第二,訂單實時處理是整個鏈路的中心,我們將這個過程定義得盡量簡潔。一筆交易中,訂單被推進得越復雜,說明系統(tǒng)設計得越復雜,出問題的概率也會越高。所以我們希望訂單核心流程非常簡單、輕薄,把復雜的東西剝離出來,把簡單和復雜明確成兩個部分。
第三,考慮到交易的時效性和異常場景越來越復雜,將交易分成正向交易流程和逆向交易流程兩個部分。正向交易流程,99%的訂單會根據這個流程走完生命周期;逆向交易流程,比如說退單要求時效性比較低,處理會牽扯多方業(yè)務可能很復雜,所以通過一個逆向的交易流程來解決。
第四,能夠在功能和業(yè)務上獨立的部分,盡可能抽象為單獨的模塊或服務。簡單來說,比如催單的服務,它其實對交易鏈路無法起到推進作用,它只是一個動作或者附帶服務,我們把它單獨抽象出來,為后面的發(fā)展做出鋪墊。
基于這些之后,我們對訂單進行完整的認知,對訂單的服務架構和業(yè)務架構做成圖中的樣子,大概是三層。下面一層是基本數據;中間層是正向逆向的流程、最核心的狀態(tài)和最關聯(lián)的交易鏈上耦合的服務;上層是用戶服務、商戶服務,包括跟交易鏈相關的,比如餓了么最近推出的“準時達”的服務。
我們同時對其他服務模塊也做了演進。一些是之前設計的不合理,如圖所示是當時緩存服務的邏輯架構,節(jié)點比較多。簡單解釋一下最初的做法:提交訂單的時候清除緩存,獲取訂單的時候如果沒有緩存的話,會通過消息機制來更新緩存。中間還有一個Replicator,起到重復合并的作用。
后來我們發(fā)現(xiàn),本來可以輕量級實現(xiàn)的內容,但是用了相對復雜的實現(xiàn),鏈路長,組件多,受網絡影響非常大。一旦一個節(jié)點緩存數據不一致,感知會比較困難,尤其是業(yè)務體量大的時候。
業(yè)務體量小的時候同時處理的量并不多,問題曝露并不明顯,但是體量變大的時候,這個設計立刻帶來很多困擾。所以我們對緩存做了簡化,就是把不必要的內容砍掉,做一個最基本的緩存服務。
這是一個最基本的緩存的套路,在數據庫更精準的情況下更新緩存,如果從DB獲取不到就從緩存獲取。這個架構雖然簡單了,但是效率比之前高很多,之前數據庫和緩存之間延遲在200毫秒左右,而這個簡單實現(xiàn)延遲控制在10毫秒以內。
之前訂單最大的瓶頸是在數據庫,我們主要做了DAL中間層組件。圖中這個中間件對我們影響非常大,日均300萬單的時候數據庫量比較大,引入DAL中間件做什么呢?有幾個作用:數據庫管理和負載均衡以及讀寫分離,水平分表對用戶和商戶兩個維度做評估,為用戶存儲至少半年以上的數據。解決了數據庫的瓶頸,系統(tǒng)整體負載能力提升了很多。
這張圖說明了訂單具體改造的時候DAL中間件起的作用,有讀寫分離端口、綁定主庫端口、水平分表、限流削峰以及負載均衡等功能。
監(jiān)控和告警的峰值非常明顯,午間和晚間兩個高峰,其他時間流量相對平緩。下面主要講三個部分。
第一,對于訂單而言,吞吐量是最需要重點關注的指標。一開始對業(yè)務指標的感知并不是特別清晰,就在某一個接口耗費了很多時間。后來發(fā)現(xiàn)一些很小BD的問題不太容易從小接口感知到,但是從業(yè)務方面感知就比較明顯,所以就更多關注業(yè)務指標的控制。
第二,通常我們重視系統(tǒng)指標,而容易忽視業(yè)務指標,其實業(yè)務指標更能反映出隱晦的問題。
第三,目前我們致力于基于監(jiān)控和數據學習的過載保護和業(yè)務自動降級。雖然現(xiàn)在還沒有完全做好,但是已經能感覺到一些效果。如果商戶長時間不接單,用戶會自動取消訂單,自動取消功能的開關目前是人工控制的,我們更希望是系統(tǒng)來控制。
比如說有大量訂單取消了,有可能是接單功能出了問題,就需要臨時關閉這個功能,如果還是依靠人來做,往往已經來不及,這時候就應該基于數據的學習,讓系統(tǒng)自動降級這個功能。
當做完這一切,訂單的架構就變成了上面這個樣子。我們把整個Service集群做了分組,有面向用戶的、面向商戶的,還有物流和其他方面的。
Design for failure
就訂單系統(tǒng)而言主要有以下四個內容。第一是消息廣播補償,第二是主流程補償,第三是災備,第四是隨機故障測試系統(tǒng)。
首先是消息廣播補償。對于訂單來說,MQ是非常核心的基礎組件,如果它出現(xiàn)問題,一些訂單處理就會受影響。為了避免這種情況發(fā)生,我們做了一個補償的內容,這個補償其實很簡單,就是在訂單狀態(tài)發(fā)生消息變化的時候,我們會同時落一份消息數據,目前會存儲最近一小時的消息。
如果MQ系統(tǒng)或者集群當前有問題或者抖動,消息廣播補償可以起到一個備線的作用。消息廣播補償不能應付所有問題,但是對于訂單系統(tǒng)的穩(wěn)定和健壯而言還是非常有用的。
第二是主流程的補償。什么是主流程?就是交易的正向流程。99%的交易都會是正向的,就是下單、付款,順利吃飯。在這個過程中,只要用戶有通過餓了么吃飯的意向,就盡全力一定讓他完成最終的交易,不要因為系統(tǒng)的原因影響到他。
主流程主要是針對鏈路本身出問題的情況,以最大程度保證交易的進行,也是對主要鏈路的保護。
比如有一次出現(xiàn)這個問題:用戶已經支付過了,但訂單沒有感受到這個結果,訂單顯示還在待支付,當時支付服務本應該把結果推送過來,訂單就可以繼續(xù)往前走,但是系統(tǒng)在那里卡住了,這對用戶就是比較差的體驗。
所以后來我們做了主流程的補償,以確保交易的信息鏈路一直完整。我們的原則是,對訂單的各個狀態(tài)變更進行推送或拉取,保證最終的一致性,鏈路和介質要獨立于原流程。我們從兩個方面來解決這個問題。
在部署方面,把提供補償功能的服務和主服務分開部署,依賴的服務也需要使用獨立實例,以保證高可用性。在效果方面,用戶支付成功前的所有信息都應該盡量入庫,可以對支付、待接單、接單等一系列環(huán)節(jié)都可以做補償。
這是主流程補償的圖,最大的關聯(lián)方就是支付和商戶,支付就是代表用戶。商戶有推送訂單信息,支付也有推送訂單信息,如果出現(xiàn)問題,補償服務可以拉取結果,訂單甚至可以自動接單。這個補償經過多次的演變,目前依然在運作,對于一些比較特殊的情況還是很有用的,可以在第一時間處理問題,保證交易的完成。
第三是災備。目前訂單系統(tǒng)做了一個比較簡單的災備,就是兩個機房的切換。切換的時候是全流量切換的,我們會把流量從A機房切到B機房。訂單的主要操作是在切換的過程中要進行修復數據。
比如,一些訂單開始是在A機房,被切換到B機房去操作,這就可能會造成兩個機群數據不一致的情況,所以會專門對信息做補全,當一筆交易切換到另一個機房后,我們要確保短時間內將數據對比并修復完成,當然主要還是確保數據最終一致。
第四是隨機故障測試系統(tǒng)。左圖是Netflix的猴子家族,右圖是我們做的Kennel系統(tǒng),一個是猴子窩,一個是狗窩。大家對猴子家族了解嗎?Netflix現(xiàn)在幾乎把所有內容都部署在云上,對系統(tǒng)和架構的要求很高,他們可以隨時破壞一些節(jié)點,以測試是否能依然為用戶提供服務。
我們也參考他們的做法,有很大的啟發(fā),避免失敗最好的辦法就是經常失敗。餓了么的發(fā)展速度比非常快,技術還不完善,設計也會有缺口。我個人覺得,一個好的系統(tǒng)或者好的設計不是一開始被大牛設計出來的,一定是隨著發(fā)展和演進逐漸被迭代出來的。
參考了Netflix的猴子家族,我們研發(fā)了自己的Kennel系統(tǒng)。猴子家族主要是針對節(jié)點的攻擊,我們的Kennel主要是對網絡、內存等做了調整,還結合自己的服務,對應用和接口也做了一些攻擊。攻擊分兩部分。
第一部分是物理層面的,我們可以對指定節(jié)點IO做攻擊,或者把CPU打到很高;對于服務和接口而言,可以把某個接口固定增加500毫秒或者更要的響應延遲,這樣做的目的是什么?在整個鏈路中,我們希望架構設計或者節(jié)點都是高可用的,高可用就需要被測試,通過大量的測試人為攻擊節(jié)點或者服務,來看預先設計好的那些措施或者補償的能力是不是真的有用。
整個Kennel的設計是,首先會有一個控制中心來做總的調度,配置模塊可以配置各種計劃,可以控制CPU或者網絡丟包等,可以設置在每周六8-10am的某個時間點攻擊系統(tǒng)十五分鐘。它還有一些操作模塊,比如執(zhí)行計劃模塊、任務執(zhí)行模塊、節(jié)點管理模塊、執(zhí)行記錄模塊等。
Kennel有四個主要的作用。
首先,幫助我們發(fā)現(xiàn)鏈路中隱蔽的缺陷,將小概率事件放大。比如說緩存不一致的問題,之前極少出現(xiàn),一旦出現(xiàn)之后,處理手段比較缺乏,那就可以通過Kennel來模擬。網絡的抖動是很隨機的,那么Kennel可以在某個時間段專門進行模擬,把小概率事件放大。如果懷疑某個地方出了問題,可以通過它來測試是不是真的能查出問題。
第二,重大功能可以在發(fā)布之前通過其進行測試,迫使你更深入地設計和編碼。通過模擬流量或者線上流量回放,來檢驗系統(tǒng)運行是否如你設計那樣工作,比如監(jiān)控的曲線或者告警以及相關服務之間的依賴等。
第三,我們做了很多失敗的準備和設計,要看到底會不會起作用、起多大作用。可以通過Kennel進行校驗,在某個時間通過隨機手段攻擊相關服務,服務方不知道具體的攻擊內容,這時原本設定的監(jiān)控告警,降級熔斷等措施有沒有及時起作用就是一個很好的校驗。同時還可以檢驗之前準備的容錯或者補償措施是否能按照預期工作。
第四,需要驗證FailOver的設計,只有驗證通過才可以依靠。所有的設計都是經歷了一次一次的失敗,一些設計原以為有用,但是真實問題發(fā)生時并沒有起到作用。真正有意義的FailOver設計一定是經過驗證的。
- 美媒聚焦比亞迪“副業(yè)”:電子代工助力蘋果,下個大計劃瞄準AI機器人
- 微信零錢通新政策:銀行卡轉入資金提現(xiàn)免手續(xù)費引熱議
- 消息稱塔塔集團將收購和碩印度iPhone代工廠60%股份 并接管日常運營
- 蘋果揭秘自研芯片成功之道:領先技術與深度整合是關鍵
- 英偉達新一代Blackwell GPU面臨過熱挑戰(zhàn),交付延期引發(fā)市場關注
- 馬斯克能否成為 AI 部部長?硅谷與白宮的聯(lián)系日益緊密
- 余承東:Mate70將在26號發(fā)布,意外泄露引發(fā)關注
- 無人機“黑科技”亮相航展:全球首臺低空重力測量系統(tǒng)引關注
- 賽力斯發(fā)布聲明:未與任何伙伴聯(lián)合開展人形機器人合作
- 賽力斯觸及漲停,汽車整車股盤初強勢拉升
免責聲明:本網站內容主要來自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網站出現(xiàn)的信息,均僅供參考。本網站將盡力確保所提供信息的準確性及可靠性,但不保證有關資料的準確性及可靠性,讀者在使用前請進一步核實,并對任何自主決定的行為負責。本網站對有關資料所引致的錯誤、不確或遺漏,概不負任何法律責任。任何單位或個人認為本網站中的網頁或鏈接內容可能涉嫌侵犯其知識產權或存在不實內容時,應及時向本網站提出書面權利通知或不實情況說明,并提供身份證明、權屬證明及詳細侵權或不實情況證明。本網站在收到上述法律文件后,將會依法盡快聯(lián)系相關文章源頭核實,溝通刪除相關內容或斷開相關鏈接。