閱讀602 返回首頁    go 阿裏雲 go 技術社區[雲棲]


餓了麼:日訂單量超900萬的架構設計及演進之路

網站在剛開始的時候大概隻是一個想法:一個產業的模型,快速地將它產生出來。“快”是第一位的,不需要花太多精力在架構設計上。在網站進入擴張期才需要對架構投入更多的精力來承載網站在爆發時的流量。

 

餓了麼成立已經8年,現在日訂單量突破900萬,我們也有了較為完善的網站架構。

 

一、網站基礎架構

 

初期,我們使用了能夠更容易拓展SOA的框架。我們用SOA的框架解決兩件事情:

 

1. 分工協作

 

網站初期,程序員可能就1~5個,那時大家忙同一個事情就可以了。彼此之間的工作都互相了解,往往是通過“吼”的方式就把問題解決了。
 

但隨著人員的增加,這種方式顯然是不行的,不可能一個人更新了代碼再把其他人的所有代碼重新上線一遍吧?於是就要考慮分工協作的問題。

 

2. 快速擴展

 

以前訂單量可能從1k到1w,雖然增長了10倍,但是總量並不是很高,對於一個網站的壓力來說,也不是那麼大。真正到了訂單量從10w到100w,從100w到 200w的時候,可能數字上隻是擴大了10倍,但對整個網站的架構上來說卻是一個巨大的挑戰。

 

我們的背景就是2014年的100萬突破到現在的900萬,技術團隊由剛開始的30多個人,到現在已經是超過900人的團隊。這時候分工協作是個巨大的挑戰。服務的分分合合,團隊的分分合合,這都需要一套框架體係來支撐,這也是SOA框架的一個作用。

 

看一下我們的現狀,中間是我們整個架構的體係,右側是和服務化相關的一些基礎,包括基礎的組件或者服務。

 

20170509094533498.jpg

 

先說語言,我們原來的網站是在PHP上的,後來慢慢轉型。

 

創始人都是大學生創業,那麼理所當然Python是一個很好的首選。到現在 Python也是很好的選擇,但是我們為什麼要擴展到Java和Go呢?

 

Python很多人都會寫,但是真正能把它做得很好的人並不多。隨著業務的發展,需要更多的開發人員。考慮到Java成熟的生態環境,以及新興的Go生態,我們最終選擇了Python、Java、Go多語言共存的一個生態。

 

WebAPI主要做一些HTTPS卸載、限流,還有安全校驗等一些通用的和業務邏輯無關的操作。

 

Service Orchestrator是服務編排層,通過配置的方式實現內外網的協議轉換、服務的聚合裁剪。

 

架構圖右邊是一些圍繞這些服務化框架的輔助係統,比如說用於定期執行一個任務的Job係統。我們有將近快1000個服務,這些係統怎麼監控?所以必須有一套監控係統。剛開始隻有30多個人時,我們更擅長的是跑到機器上去搜一下Log,但到了900多人時,你不可能都到機器上去搜一遍Log,需要有個集中式的日誌係統。其它的係統這裏就不一一贅述了。

 

羅馬不是一天建成的,基礎架構是個演進的過程。我們精力有限,那先做什麼呢?

 

二、服務拆分

 

當網站變大了,原來的架構跟不上發展的節奏了。我們要做的第一件事情就是:

把大Repo拆成一個小Repo,把大服務拆成小服務,把我們的集中基礎服務,拆分到不同的物理機器上去。

 

光是服務拆分用了一年多的時間才做完,這是一個比較漫長的過程。

 

這個過程中,首先要對API做一個很好的定義。因為一旦你的API上線之後,再做一些修改的成本是相當大的。會有很多人依賴於你的API,很多時候你也並不知道有誰依賴於你的API,這是一個很大的問題。

 

然後再把一些基礎服務抽象出來。很多原來的服務其實是耦合在原來的業務代碼裏麵的。比如說支付業務,業務很單一時,緊耦合的代碼沒有關係,但是擴展出的越來越多的業務都需要支付服務時,你每一個業務(比如說支付的功能)都要去做一個嗎?所以我們要把這些基礎服務抽離出來,比如支付服務、短信服務、推送服務等。

 

拆服務看似很簡單、沒什麼價值,但這恰恰是我們剛開始就要做的事情。其實在這個時期,前麵所有的那些架構都可以往後拖,因為不做架構調整其實不會死人,但是拆服務你不做的話,真的會死人。

 

服務拆分必定是一個漫長的過程,可這實際是一個很痛苦的過程,也需要很多配套係統的係統工程。

 

三、發布係統

 

發布是最大的不穩定因素。很多公司對發布的時間窗口有嚴格的限定,比如說:

  • 每周隻有兩天可以發布;

  • 周末是絕對不可以發布的;

  • 業務的高峰期絕對不允許發布;

  • 等等……

 

我們發現,發布的最大問題在於發布上去之後沒有簡單可執行的回退操作。回退操作到底是誰來執行,是發布人員就可以執行,還是需要專人來執行?如果是發布人員的話,發布人員並非24小時在線工作,出了問題找不到人怎麼辦?如果是有專人來執行回退,而又沒有簡單、統一的回退操作,那這個人需要熟悉發布人員的代碼,這基本上不可行。

 

所以我們就需要有發布係統,發布係統定義了統一的回退操作,所有服務必須遵循發布係統的定義回退操作。

 

在餓了麼對接發布係統是對所有人的強製要求,所有的係統必須全部接入發布係統。發布係統的框架很重要,這個東西其實對於公司是很重要的一件事情,需要放到第一優先級的隊列裏麵去考慮。

 

四、服務框架

 

緊接著就是餓了麼的服務框架,把一個大的Repo拆分成一個小的Repo,把一個大的服務拆成一個小的服務,讓我們的服務盡量獨立出去,這需要一套分布式服務框架來支撐。

 

分布式服務框架包含的服務注冊、發現、負載均衡、路由、流控、熔斷、降級等功能,這裏就不一一展開了。前麵已經提及,餓了麼是多語言的生態,有 Python的,也有Java的,我們的服務化框架對應也是多語言的。這對我們後來一些中間件的選型是有影響的,比如說DAL層。

 

五、DAL數據訪問層

 

當業務量越來越大的時候,數據庫會變成一個瓶頸。

 

前期可以通過提升硬件的方式來提升數據庫的性能。比如:

  • 升級到一個有更多CPU的機器;

  • 把硬盤改成 SSD 的或者更高級一點的。

 

但硬件提升終歸是有一個容量限製的。而且很多做業務的小夥伴,寫代碼的時候都直接操作數據庫,發生過很多次服務一上線數據庫就被打爆的情形。數據庫被打爆掉了之後,除非等待數據庫恢複,沒有任何其它機會可以恢複業務。

 

如果數據庫裏麵數據是正常的,業務其實都可以補償出來。所以我們做DAL服務層的時候,第一件事情是限流,其它的東西可以放一放。然後做連接複用,我們Python框架用的多進程單線程加協程的模型。

 

多進程之間其實是不可以共享一個連接的。比如:一台機器上部署了10個 Python進程,每個進程10個數據庫連接。再擴展到10台機器上,就有1000個數據庫連接。對數據庫來說,連接是一個很昂貴的東西,我們DAL層要做一個連接複用。

 

這個連接複用講的不是服務本身的連接複用,而是說DAL層上的連接複用,就是服務有1000個連接到DAL層,經過連接複用後對數據庫可能隻是保持著十幾個連接。一旦發現某個數據庫請求是一個事務的話,那麼DAL就幫你保留這個連接的對應關係。當這個事務結束之後,就把數據庫的連接,放回到共用池裏麵去,供其他人使用。

 

然後做冒煙和熔斷。數據庫也可以熔斷的。當數據庫發生冒煙時,我們會殺掉一些數據庫的請求,保證數據庫不至於崩潰。

 

六、服務治理

 

服務框架之後,涉及服務治理的問題。服務治理其實是一個很大的概念。首先是埋點,你要埋很多的監控點。

 

比如有一個請求,請求成功了或者失敗了,請求的響應時間是多少,把所有的監控指標放到監控係統上麵去。我們有一個很大的監控屏幕,上麵有很多的監控指標。有專門小組72小時去盯著這個屏幕,如果有任何曲線波動了,就找人去解決。另外是報警係統,一個監控屏幕展示的東西總是有限的,隻能放那些很重要的關鍵指標。這個時候就需要有報警係統。

 

羅馬不是一天建成的,基礎架構更是一個演進的過程。我們的資源和時間總是有限的,作為架構師和 CTO 來說,如何在這種有限的資源下,產出更重要的東西?

 

我們做了很多係統,覺得自己做得很不錯了,但實則不是,我感覺我們又回到了石器時代,因為問題越來越多,需求也越來越多,總感覺你的係統裏還缺點什麼東西,想做的功能也一大堆。
 

比如對於流控係統,現在我們還是需要用戶去配一個並發數,那麼這個並發數,是不是根本不需要用戶去配?是不是可以基於我們服務本身的一個狀態自動去控製並發數?
 

然後是升級方式,SDK升級是個很痛苦的事情。比如說我們服務框架2.0發布的時候是去年12月份,到現在還有人用的是1.0。是不是可以做到SDK的無損感升級,我們自己來控製升級的時間和節奏。

 

還有,我們現在的監控隻支持同一個服務上的匯聚,是不分集群、不分機器的,那是不是以後的指標可以分集群、分機器?舉一個最簡單的例子,比如一個服務上有10台機器,那麼可能隻是某一個機器上出了問題,但它所有的指標都會平均分攤到其它的9台機器上去。你隻是看到了整個服務延時增加了,但有可能隻是某一台機器拖慢了整個服務集群。但我們現在還做不到更多維度的監控。

 

還有智能化的報警,這個報警,就是要快、全、準,我們現在做到更快了,做到更全了,怎麼才能做到更準?每天的報警量高峰時間一分鍾一千多個報警發出去。所有的一千報警都是有用的嗎?報警多了之後,就相當於沒有報警。大家都疲勞了,就不去看了。我怎麼能夠把這個報警更準確地區分出來?還有更智能化的鏈路分析?以後是不是我們的監控不要放監控指標,而是放鏈路分析,這樣就能夠很清晰地知道,這個問題對應的是哪一個結點上出了問題。

 

原文發布時間為:2017-05-09

本文來自雲棲社區合作夥伴DBAplus

 

最後更新:2017-05-17 14:02:14

  上一篇:go  MySQL索引設計背後的數據結構及算法詳解
  下一篇:go  從0到1構建數據生態係列(二):拆解架構藍圖