重構購物車的過程

背景


公司目前所有的核心業務都在一個應用中,屬於單體應用,隨着業務的快速發展和開發人員的不斷增加,單體應用的開發模式就開始暴露出各種各樣的問題。經歷過這個過程的開發人員,想必深有體會。

我舉個最近才遇到的一個例子。今年的八月份,有5個大需求要在同一天上線,由於只有一個單體應用,5個需求又都不同,因此拉了5個開發分支,對應5個需求。好了,在八月的某個相同時間段,各個測試人員都要開始測試自己負責的模塊了,測試環境不夠用(部分創業公司裏,測試環境經常不夠用,也沒有用docker),開發人員只能臨時用了一個分支,叫xxxxx_all分支,將需要測試的所有分支都合併到這個分支裏,這是沒有辦法中的辦法了,但是會出現各種問題:

開發人員在自己的分支裏改完代碼後,每次都需要合併到xxxxx_all分支,經常出現衝突,因爲大家都往all分支合併代碼。解決衝突的過程非常消耗時間,因爲衝突的代碼不是自己寫的,不敢隨便合併,只能叫其他開發人員過來協助。另外,合併代碼的時候,很容易一不小心就將別人代碼覆蓋掉,導致測試人員經常發飆,剛剛還好好的功能,又不行了。

可能有網友會問,幹嘛不直接在xxxxx_all分支上開發呢?這樣做有個風險,因爲在創業公司裏,老闆經常會突然叫停某個功能的,不想上了。這個時候,如果都在xxxxx_all分支上開發,就得把部分需求的代碼剝離出來,也很麻煩的。萬一剝離不乾淨,上線了,那就悲催了。

那麼在這次的購物車重構項目,也是八月份上,但是由於購物車抽取成微服務了,是單獨的應用了,使用單獨的分支,沒有跟原來的單體應用的代碼混合在一起,算是倖免於難了,爽爽的在自己分支上不斷提交即可。

隨着業務不斷髮展和開發人員不斷增多,單體應用的問題不只是上面提到的開發模式問題,更爲要命的是,這個單體應用某個模塊的某一點代碼有問題,會拖垮和影響其他的核心模塊。另外,整個單體應用中的某個不重要的模塊的代碼修改了,就得上線,但是其實很多核心模塊並沒有改動代碼的。比如說,我們最近遇到的一個慢SQL查詢的,由於商品分類導航的SQL語句寫的非常差,稍微流量大一些,數據庫服務器CPU就hold 不住了,直接影響了下單這個核心模塊了。


關於購物車表的遷移


從單體應用到應用服務化,其中最難搞的便是數據庫。就拿購物車來說,熟悉電商購物車的都應該知道,購物車通常都有如下功能:

  • 失效列表商品:可能有部分商品下架了或者庫存爲0了,在購物車裏被列爲失效商品了;
  • 購物車列表按照營銷活動聚合,比如說,滿xx減yy這種活動,只要是這種活動的商品,都聚合在獨立的卡片裏;
  • 展示商品的基本信息;
  • 調整購物車商品數量;

從上面的業務可以看到,購物車至少要依賴如下幾個模塊:

  • 商品模塊
  • 庫存模塊
  • 營銷模塊

如果我們把購物車相關的數據庫表單獨挪動出來,成爲一個單獨的購物車數據庫,那麼購物車依賴的模塊,也必須已經抽取成微服務了,然後購物車服務通過調用接口的方式來讀取其他模塊的數據。但是,我們並沒有用這種細粒度的方式,原因有三個:

  • 購物車服務是公司的第一個微服務,在購物車之前,沒任何微服務接口的;
  • 購物車原先是PHP寫的,從維護成本和獨立性考慮,優先重構購物車;
  • 商品、庫存和營銷模塊太複雜了,改造成本極其大,且其他模塊對它們的依賴太多了,牽一動全身;

因此只能採用如下的折中方案:

購物車的數據庫表暫時不遷移出來,至於購物車依賴的商品、庫存等模塊,在購物車服務裏實現一遍。等到後續依賴的模塊抽取成微服務後,再改造一次,從本地調用改成遠程調用。

有網友可能會說,好爛的重構方案哦,確實,這是個折中的方案。但是你要知道,在老闆只給2、3個星期的期限,同時還要繼續支持其他業務開發的情況下,想要有階段性的里程碑,只能這麼幹了。不太可能有專門的重構部門和團隊,讓你好好重構個一兩年,這在創業公司裏,是不可能做到的。

另外根據我們現在公司的情況,這種方案是可行的,因爲購物車雖然依賴的商品、庫存等模塊,相關的依賴的那部分邏輯不復雜,同時,購物車流量也不是很大,暫時無需將購物車數據庫表遷移出來並分庫。


購物車數據的存儲方案


在PHP版本里的購物車中,購物車列表是從Redis讀取的,然後用戶對購物車數據進行修改的時候,刷新購物車列表的緩存數據。當時我看到這種設計後,直接就無語了。因爲在我的認知裏,購物車數據要麼純用Redis,要麼純用數據庫。像京東的購物車,全部數據都存儲在Redis裏,讀寫操作也全部基於Redis,這種方案挺好的,用Redis比用數據庫快非常多。而唯品會則是採用數據庫的,並用一百多個分庫,緩解寫的壓力。

因此我當時做的第一個決定,就是直接用數據庫即可,量大的時候,採用分庫。避免即用Redis又用數據庫這種雞肋無用的設計,同時購物車寫接口也無需維護緩存了,變得簡單了一些。


微服務框架的選擇


當時我跟老闆建議,先服務化,再微服務化。但是老闆不答應,說一步到位,直接微服務化。
我本來的想法是,先垂直拆分購物車服務,然後對外暴露http接口供調用方調用,先別用服務發現、註冊中心那套,因爲學習成本高,也不知道有啥坑。但是被否掉了,只能直接微服務化。

市面上最火的是Dubbo和Spring Cloud,Dubbo我們團隊沒人會用,而Spring Cloud團隊裏有幾個人用過,加上我們公司原本的應用就是基於Spring Boot的,因此直接採用Spring Cloud。另外,Spring Cloud的社區非常活躍,出了問題,也比較好定位。


需要一次把Spring Cloud全家桶全用上嗎


Spring Cloud還有網關、調用鏈跟蹤、監控等等組件,這些都是好東西,時間允許的情況下,當然都要用,但是如果時間太緊了,就只能先把註冊中心、服務發現、路由等必須要的組件先用上,後續再找時間補充上其他組件。另外一下子用太多,出問題了,也加大了定位問題的難度。


Spring Cloud架構部分場景驗收


當功能測試通過後,最好搞一個預發佈的環境,驗證一下Spring Cloud新架構是否能正常工作。下面是一些驗收的Case:

  • 路由功能:有多臺機器和多個微服務後,請求必須能路由到不同的機器,避免所有請求只路由到某臺機器上;
  • 註冊中心重啓;
  • 手動kill掉其中一個微服務,看看客戶端的請求會不會繼續路由到壞掉的服務上;

線上觀察


上線後的第一個星期,要經常盯着微服務,看看有沒有請求慢的、機器的CPU、內存、數據庫的負載等。

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