0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

單體架構(gòu)如何向分布式架構(gòu)轉(zhuǎn)型

jf_ro2CN3Fa ? 來源:芋道源碼 ? 作者:芋道源碼 ? 2022-10-20 10:28 ? 次閱讀


背景

我們?cè)诹募軜?gòu)風(fēng)格之前先明確一個(gè)問題,什么是架構(gòu)?我們?yōu)槭裁匆x擇架構(gòu)?用來解決哪些問題?

什么是架構(gòu)

書本定義:“軟件的架構(gòu)是一種抽象的結(jié)構(gòu),他由軟件的各個(gè)組成部分和這些部分之間的依賴關(guān)系構(gòu)成”。

我的理解是,架構(gòu)就是根據(jù)業(yè)務(wù)選擇合適的技術(shù)、中間件,并且按照合適的設(shè)計(jì)模式對(duì)這些模塊,進(jìn)行組裝來滿足業(yè)務(wù)特性的需求。

選擇架構(gòu)風(fēng)格的目的

我們選擇架構(gòu)風(fēng)格的初衷在于 “三更原則”(自己的理解) :更好地降本提效、更快地發(fā)版上線、更好地維護(hù)系統(tǒng)穩(wěn)定性。

任何一個(gè)架構(gòu)風(fēng)格,都可以實(shí)現(xiàn)功能性需求,但是一個(gè)好的架構(gòu)風(fēng)格能在功能性需求之上,提升非功能性需求。那么你可能會(huì)問,什么是非功能性需求?舉例:擴(kuò)展性、穩(wěn)定性等等。

這里將會(huì)以我認(rèn)知結(jié)合踩過的坑,來給大家詳細(xì)講一下,我們是如何從單體架構(gòu)演進(jìn)到分布式架構(gòu)。在向分布式單體架構(gòu)的演進(jìn)的道路上,又是如何進(jìn)行的抉擇,以及為什么最后同時(shí)選擇了 微服務(wù)架構(gòu)+分布式架構(gòu) 的原因。接下來就結(jié)合一個(gè)系統(tǒng)來作為案例,貫穿主線講解。

首先來講一下,最初的單體架構(gòu)的經(jīng)歷和轉(zhuǎn)型。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

單體架構(gòu)

我們?cè)谙到y(tǒng)創(chuàng)建之初,往往都是集中業(yè)務(wù)、單點(diǎn)部署系統(tǒng),所有業(yè)務(wù)打一個(gè)包,快速上線。滿足了業(yè)務(wù)初期的快速發(fā)版上線,而且適合中小公司沒有自己的 PaaS 平臺(tái),應(yīng)對(duì)初期快速迭代的業(yè)務(wù),開發(fā)、迭代、測(cè)試、發(fā)布都是非常的便捷。那么單體架構(gòu)都有什么類型呢?

單體架構(gòu)類型

單體架構(gòu)也分為大泥團(tuán)架構(gòu)、分層單體架構(gòu)、模塊化單體架構(gòu),他們的區(qū)別是什么呢?

  • 大泥團(tuán)單體架構(gòu) :毫無分層、所有模塊聚焦在一起,相互穿插(除非是你接手需要改造,否則不要?jiǎng)?chuàng)建這樣的架構(gòu)風(fēng)格。這種大泥團(tuán)架構(gòu)很難拆分,到最后的下場(chǎng)往往都是重新搭建);
  • 分層單體架構(gòu) :普遍的選擇。架構(gòu)進(jìn)行了簡(jiǎn)單的分層,比如傳統(tǒng)的 MVC 三層架構(gòu);
  • 模塊化單體架構(gòu) :一般是隨著業(yè)務(wù)的發(fā)展,由分層單體架構(gòu)演變而來,特點(diǎn)就是引入了多個(gè)業(yè)務(wù)模塊并且提供相應(yīng)的服務(wù)能力。

單體架構(gòu)的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 應(yīng)用的開發(fā)很簡(jiǎn)單
  • 易于對(duì)應(yīng)用程序大規(guī)模的更改
  • 測(cè)試相對(duì)簡(jiǎn)單、直觀
  • 部署簡(jiǎn)單明了
  • 橫向擴(kuò)展不費(fèi)吹灰之力

在業(yè)務(wù)的初期,單體架構(gòu)的優(yōu)點(diǎn),無論從哪個(gè)方面來說,都優(yōu)于其他架構(gòu)風(fēng)格,但是隨著業(yè)務(wù)的增加、耦合,單體架構(gòu)的缺點(diǎn)也逐漸暴露出來,這個(gè)也符合“康威定律”。那么單體架構(gòu)的“后期”會(huì)暴露出哪些問題呢?

缺點(diǎn)

  • 代碼庫膨脹
  • 過度的復(fù)雜性會(huì)嚇退開發(fā)者
  • 開發(fā)速度慢
  • 從代碼提交到實(shí)際部署的周期很長(zhǎng),而且容易出問題
  • 難以擴(kuò)展
  • 系統(tǒng)的穩(wěn)定性得不到保障
  • 需要長(zhǎng)期依賴某個(gè)可能過時(shí)的技術(shù)棧

單體架構(gòu)的這些缺點(diǎn),其實(shí)影響的還是我上面提到的“三更原則”。經(jīng)過上面的鋪墊,相信大家已經(jīng)對(duì)單體架構(gòu)風(fēng)格已經(jīng)有了簡(jiǎn)單的理解。

光有方法論是不行的,我們得結(jié)合項(xiàng)目以及代碼片段來加深理解,做到真正的應(yīng)用。接下來我就用一個(gè)庫存系統(tǒng)來進(jìn)行串聯(lián)進(jìn)行講解。先通過這張圖來了解下庫存系統(tǒng)是用來做什么的?

  • 創(chuàng)建之初,1 個(gè)服務(wù)提供商品庫存維護(hù)、庫存查詢、庫存扣減能力。

  • 隨著業(yè)務(wù)的發(fā)展,庫存面向多個(gè)服務(wù):B 端業(yè)務(wù),平臺(tái)內(nèi)部業(yè)務(wù)系統(tǒng)、平臺(tái)外部中臺(tái)。C 端業(yè)務(wù),訂單商品扣減庫存、網(wǎng)關(guān)查詢庫存數(shù)量。

06df0526-501e-11ed-a3b6-dac502259ad0.png

單體架構(gòu)的案例——庫存系統(tǒng)

最初的庫存代碼分層如下:

  • api :對(duì)外提供的 Dubbo 服務(wù)
  • common :封裝了公共方法
  • dao :封裝了數(shù)據(jù)庫 DHCP 交互
  • domain :實(shí)體類
  • inner-api :系統(tǒng)內(nèi)部 API 交互
  • router :廢棄
  • rpc :上下游 RCP 交互
  • service :業(yè)務(wù)邏輯層
  • web :Web 服務(wù)層
  • worker :任務(wù)調(diào)度層
07015446-501e-11ed-a3b6-dac502259ad0.png

在最初很長(zhǎng)的一段時(shí)間里,我們部署了兩個(gè)單體服務(wù)。一個(gè)是 API 接口來保障上游的庫存查詢以及調(diào)用,另一個(gè)是 Web 服務(wù)的后臺(tái)管理平臺(tái)。這兩個(gè)單體服務(wù)很好的貼合了最初的業(yè)務(wù)迭代和發(fā)版速度,但是后來隨著業(yè)務(wù)的增加附加調(diào)用量的增加,單體服務(wù)的無論是從性能和穩(wěn)定性都出現(xiàn)了較大的波動(dòng)。

意料之外,情理之中的事故慘案

2015 年 6 月 26 日晚,也是一個(gè)促銷活動(dòng)的前夕,庫存的 Web 管理平臺(tái)掛了,原因就是大量庫存導(dǎo)入,服務(wù)器的內(nèi)存不足導(dǎo)致機(jī)器宕機(jī)。商家、運(yùn)營(yíng)無法通過導(dǎo)表的方式去維護(hù)庫存數(shù)量,在這之前已經(jīng)經(jīng)歷過了多次橫向擴(kuò)容。還是出現(xiàn)了預(yù)料之外的流量和穩(wěn)定性的問題。

0711fe90-501e-11ed-a3b6-dac502259ad0.png

而且在接下來的大促過程當(dāng)中,庫存的單體服務(wù) API 接口也承受了非常大的壓力。

一方面是上游調(diào)用方有很多,比如 App 端首頁中的門店網(wǎng)關(guān),查詢商品是否有庫存,是否展示。購物車加車,也會(huì)查詢商品庫存的數(shù)量,提單則會(huì)對(duì)庫存數(shù)量進(jìn)行扣減,乃至后續(xù)的訂單取消同樣也會(huì)調(diào)用庫存接口。

另一方面大的 KA 商家通過中臺(tái)對(duì)接對(duì)庫存進(jìn)行操作,為了盡可能的讓商家門店的庫存和線上平臺(tái)的庫存保持一致,減少線上線下庫存不一致導(dǎo)致的超賣、少賣。中臺(tái)同步間隔時(shí)間都非常短,5 分鐘~10 分鐘就要全量同步一次。后續(xù)隨著入駐的商家增多,這個(gè)量級(jí)增長(zhǎng)得也非常的迅速。于是我們開啟了單體服務(wù)向分布式服務(wù)演進(jìn)的大門。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

分布式架構(gòu)

分布式架構(gòu)的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 可用性高
  • 可擴(kuò)展性高
  • 系統(tǒng)容錯(cuò)性高
  • 業(yè)務(wù)代碼可讀性高
  • 維護(hù)簡(jiǎn)單

這些優(yōu)點(diǎn)正是我們當(dāng)時(shí)庫存系統(tǒng)欠缺的,尤其是其中的可用性、系統(tǒng)容錯(cuò)性,是我們系統(tǒng)演進(jìn)迭代的首要目標(biāo)。

《分布式架構(gòu)體系》中描述到,分布式架構(gòu)的核心理念也是按照(功能、業(yè)務(wù)、領(lǐng)域等)對(duì)系統(tǒng)進(jìn)行拆分,通過合理的拆分結(jié)構(gòu),實(shí)現(xiàn)各業(yè)務(wù)模塊的解耦,同時(shí)通過系統(tǒng)級(jí)容錯(cuò)設(shè)計(jì),在廉價(jià)硬件基礎(chǔ)設(shè)施上構(gòu)建起高可用、可擴(kuò)展的開放技術(shù)體系。

所以我們庫存系統(tǒng)到底要按照什么進(jìn)行拆分,功能?業(yè)務(wù)?領(lǐng)域?在拆分之前我們一定要明確設(shè)計(jì)的目標(biāo),避免目標(biāo)方向錯(cuò)誤帶來的人力、成本資源的浪費(fèi)。在弄清楚目標(biāo)之前,我們先了解下分布式架構(gòu)的缺點(diǎn),通過了解這些缺點(diǎn)來衡量滿足我們目標(biāo)的前提下,需要進(jìn)行哪些方面的取舍,就如 CAP 原則一樣,只能滿足其中的兩個(gè),AP 或者 CP。

2)分布式架構(gòu)的缺點(diǎn)

  • 服務(wù)多,人員對(duì)拆分后的業(yè)務(wù)模塊理解要花費(fèi)一些成本
  • 技術(shù)棧升級(jí)耗費(fèi)人力
  • 分布式事務(wù)的保持
  • 業(yè)務(wù)模塊之間的 RPC 交互損耗

庫存系統(tǒng)的特點(diǎn),高可用、高并發(fā)、強(qiáng)數(shù)據(jù)一致性。接下來我們就來講一下,庫存是如何從單體架構(gòu)向分布式架構(gòu)進(jìn)行的轉(zhuǎn)型。

單體架構(gòu)如何向分布式架構(gòu)轉(zhuǎn)型

因?yàn)閹齑婷媾R的最大的問題是穩(wěn)定性,所以我們首先針對(duì)功能進(jìn)行了拆分。

1)功能拆分

這一步是相對(duì)簡(jiǎn)單的,我們梳理出庫存面向服務(wù)的業(yè)務(wù)方進(jìn)行服務(wù)劃分。這部分無需進(jìn)行太多代碼的改造,一套接口通過變更不同的 group 別名,部署到不同的集群即可。

074d9108-501e-11ed-a3b6-dac502259ad0.png

拆分后,不同的服務(wù)應(yīng)對(duì)不同的業(yè)務(wù)方,系統(tǒng)錯(cuò)誤的隔離性好,不會(huì)說出現(xiàn)一損俱損的局面,穩(wěn)定性上也有了保障。在解決了穩(wěn)定性的問題后,留給我們了一些喘氣的間隔,可以有時(shí)間去進(jìn)行代碼的優(yōu)化。因?yàn)閯偛乓蔡岬搅?,我們只是通過分布式的集群部署來解決容錯(cuò)性的問題,但是代碼還是一套,臃腫的代碼也會(huì)拖慢我們的開發(fā)上線速度。那么接下來要進(jìn)行的就是,對(duì)業(yè)務(wù)代碼的解耦,這塊也是難度最高的。我們是如何做的呢?

2)業(yè)務(wù)拆分

業(yè)務(wù)拆分的思路是什么呢?

  • 以業(yè)務(wù)本身為導(dǎo)向,充分了解系統(tǒng)業(yè)務(wù)模型,劃分業(yè)務(wù)邊界;
  • 業(yè)務(wù)依賴的范圍,細(xì)分功能,盡量減少功能之間的重復(fù)依賴;
  • 根據(jù)拆分功能的影響大小進(jìn)行評(píng)估,拆小保大;
  • 拆分的過程中不要修改業(yè)務(wù)邏輯,不要進(jìn)行拆分之外的任何優(yōu)化動(dòng)作(除非是bug)。

基于上述拆分的思路,庫存系統(tǒng)又是如何劃分的業(yè)務(wù)模塊呢?動(dòng)了哪些代碼?

3)如何劃分業(yè)務(wù)模塊

關(guān)于業(yè)務(wù)劃分,網(wǎng)上有很多方法論,事件風(fēng)暴法、四色建模法等等,但是萬法不離其宗,那就是圍繞事件。以庫存系統(tǒng)舉例:庫存初始化(門店 + sku 庫存創(chuàng)建)、庫存數(shù)量維護(hù)(修改現(xiàn)貨數(shù)量、修改可售狀態(tài))、扣減業(yè)務(wù)(購物車扣減、提單扣減、訂單取消扣減)、提醒業(yè)務(wù)(缺貨提醒)等。每一個(gè)事件都有獨(dú)立的鏈路軸,以及時(shí)間線可以形成閉環(huán)。

075a0a1e-501e-11ed-a3b6-dac502259ad0.png078961ba-501e-11ed-a3b6-dac502259ad0.png07a6ad1a-501e-11ed-a3b6-dac502259ad0.png

4)如何在原有模塊上拆分

大多數(shù)單體架構(gòu)都是面向過程的設(shè)計(jì),domain 層充斥這個(gè)各種 DTO、VO、BO,所以在層與層的數(shù)據(jù)交互過程中,大都是經(jīng)歷了多次的 POJO。另外就是 Service 層充斥著和 DAO 層數(shù)據(jù)交互以及參雜了業(yè)務(wù),而且嚴(yán)重違反了依賴倒置原則,整個(gè)層變得非常的沉重。這里舉個(gè)例子:

  • 同層級(jí)間相互引用
  • Service 層包含了太多業(yè)務(wù)邏輯,無法保障原子性
07eff7ae-501e-11ed-a3b6-dac502259ad0.png

這里截取部分代碼片段作為案例,來講述下我們?cè)诓鸱謽I(yè)務(wù)的過程中,需要做一些什么操作。

  • 對(duì) Service 層進(jìn)行 CQS 的拆分
  • 把業(yè)務(wù)邏輯從原有的 Service 層抽離,保障 Service 方法遵循 SRP 原則
  • 新增業(yè)務(wù)聚合層(或者向六邊形架構(gòu)里提到的 adapter 轉(zhuǎn)接口)來聚合 Service 層的方法
081d378c-501e-11ed-a3b6-dac502259ad0.png

原始代碼

@Service
publicclassSkuMainServiceImplimplementsSkuMainService{
privatestaticfinalorg.slf4j.LoggerLOGGER=LoggerFactory.getLogger(SkuMainServiceImpl.class);

@Resource
privateSkuMainDaoskuMainDao;
@Resource
privateZkConfManagerCenterServicezkConfManagerCenterService;
@Resource
privateProductImagesServiceproductImagesService;//同級(jí)互相引用,未遵循依賴倒置
@Resource
privateMqServicemqServiceImpl;

@Value("${system.group.environment}")
privateStringsystemGroupEnvironment;
/**
*問題:service層聚合了太多業(yè)務(wù)邏輯倒置上層方法沒辦法統(tǒng)一
*@paramskuMainInfoMQEntity
*@throwsException
*/
publicvoideditorSaveProuct(SkuMainInfoMQEntityskuMainInfoMQEntity)throwsException{
try{
SkuMainBeanskuMainBean=skuMainInfoMQEntity.getSkuMainBean();
if(skuMainBean==null){
thrownewException("修改參數(shù)為空!");
}

SkuMainBeanoriginalSku=this.getSkuMainBeanBySkuId(skuMainBean.getId());
if(originalSku==null){
thrownewException("無效SkuId!");
}

SkuMainBeanskuMainUpdate=updateIsWeightMark(skuMainBean);
SkuMainBeanskuMainPre=this.get(skuMainUpdate.getId());
//系統(tǒng)下架的商品強(qiáng)制下架
if(skuMainPre!=null&&skuMainPre.getSystemFixedStatus()!=null&&skuMainPre.getSystemFixedStatus().equals(SystemFixedStatusEnum.SYSTEM_FIXED_STATUS_DOWN.getCode())){
skuMainUpdate.setFixedStatus(FixedStatusEnum.PRODUCT_DOWN.getCode());
}

booleanflag=skuMainDao.editorProduct(skuMainUpdate);
if(flag){
if(!zkConfManagerCenterService.isDefaultStoreStatisticsScore(skuMainBean.getOrgCode())){
SkuMainBeansaveSkumainBean=this.get(skuMainUpdate.getId());
//防止未查到,把緩存覆蓋
if(saveSkumainBean!=null){
cacheSkuMainBean(saveSkumainBean);
}
//發(fā)送Sku修改MQ
sendSkuModifyMq(SkuModifyOpSourceEnum.MIX_UPDATE_SKU,originalSku,newSkuMainInfoMQEntity(skuMainUpdate));
ProductImagesBeanproductImagesBean=productImagesService.queryImagesBySkuId(skuMainUpdate.getId());
SkuMainInfoCheckMQEntityskuMainInfoCheckMQEntity=newSkuMainInfoCheckMQEntity();
skuMainInfoCheckMQEntity.setSkuMainBean(skuMainUpdate);
skuMainInfoCheckMQEntity.setProductImagesBean(productImagesBean);
mqServiceImpl.sendJosMQ(skuMainInfoCheckMQEntity,MqTypeEnum.RcsKeyWordsCheck);
mqServiceImpl.sendJosMQ(skuMainInfoCheckMQEntity,MqTypeEnum.SenseKeyWordsCheck);
}else{
LOGGER.info("addopenplatformsku,notnotnotsendmq!skuId={}",skuMainBean.getId());
}
}
}catch(Exceptione){
LOGGER.error("修改商品信息失敗.e:",e);
thrownewException(e);
}
}

CQS 和 SRP 的改造,拆解 GOD Classes

Read 服務(wù)

082efc4c-501e-11ed-a3b6-dac502259ad0.png

Write 服務(wù)

085fe26c-501e-11ed-a3b6-dac502259ad0.png

抽離到業(yè)務(wù)層 Business 層后

@Service
publicclassSkuMainBusinessServiceImplimplementsSkuMainBusinessService{
privatestaticfinalorg.slf4j.LoggerLOGGER=LoggerFactory.getLogger(SkuMainBusinessServiceImpl.class);

@Resource
privateZkConfManagerCenterServicezkConfManagerCenterService;
@Resource
privateMqServicemqService;
@Resource
privateSkuMainReadserviceskuMainReadservice;
@Resource
privateSkuMainWriteserviceskuMainWriteservice;

@Value("${system.group.environment}")
privateStringsystemGroupEnvironment;
/**
*問題:service層聚合了太多業(yè)務(wù)邏輯倒置上層方法沒辦法統(tǒng)一
*@paramskuMainInfoMQEntity
*@throwsException
*/
publicvoideditorSaveProuct(SkuMainInfoMQEntityskuMainInfoMQEntity)throwsException{
try{
SkuMainBeanskuMainBean=skuMainInfoMQEntity.getSkuMainBean();
if(skuMainBean==null){
thrownewException("修改參數(shù)為空!");
}
SkuMainBeanoriginalSku=skuMainReadservice.getSkuMainBeanBySkuId(skuMainBean.getId());
if(originalSku==null){
thrownewException("無效SkuId!");
}
SkuMainBeanskuMainUpdate=skuMainWriteservice.updateIsWeightMark(skuMainBean);
SkuMainBeanskuMainPre=skuMainReadservice.queryDbById(skuMainUpdate.getId());
//系統(tǒng)下架的商品強(qiáng)制下架
if(skuMainPre!=null&&skuMainPre.getSystemFixedStatus()!=null&&skuMainPre.getSystemFixedStatus().equals(SystemFixedStatusEnum.SYSTEM_FIXED_STATUS_DOWN.getCode())){
skuMainUpdate.setFixedStatus(FixedStatusEnum.PRODUCT_DOWN.getCode());
}
booleanflag=skuMainWriteservice.editorProduct(skuMainUpdate);
if(flag){
if(!zkConfManagerCenterService.isDefaultStoreStatisticsScore(skuMainBean.getOrgCode())){
SkuMainBeansaveSkumainBean=skuMainservice.queryDbById(skuMainUpdate.getId());
//防止未查到,把緩存覆蓋
if(saveSkumainBean!=null){
skuMainWriteservice.cacheSkuMainBean(saveSkumainBean);
}
//發(fā)送Sku修改MQ
skuMainWriteservice.sendSkuModifyMq(SkuModifyOpSourceEnum.MIX_UPDATE_SKU,originalSku,newSkuMainInfoMQEntity(skuMainUpdate));
}else{
LOGGER.info("addopenplatformsku,notnotnotsendmq!skuId={}",skuMainBean.getId());
}
}
}catch(Exceptione){
LOGGER.error("修改商品信息失敗.e:",e);
thrownewException(e);
}
}

構(gòu)建好的業(yè)務(wù)層

087b2a36-501e-11ed-a3b6-dac502259ad0.png

拆分小結(jié)

拆分到這里,業(yè)務(wù)層的劃分基本就比較清晰了。而且在這個(gè)增量整合底層代碼的過程中,面向過程的業(yè)務(wù)線也都梳理的比較清晰了,底層方法也都提取到了業(yè)務(wù)層收口,通過接口對(duì)外提供服務(wù)。那么接下來我們要面臨的問題就是,如何對(duì)具體的讀寫進(jìn)行拆分。

基于 CQRS 打造分布式服務(wù)

上面我們也提到了,進(jìn)行了整體功能的拆分,并沒有對(duì)具體的讀寫服務(wù)的拆分。在面向服務(wù)的場(chǎng)景下,功能里也是分讀服務(wù)、寫服務(wù)。那么我們有什么原則來指導(dǎo)讀寫服務(wù)的分離么?那就是 CQRS 的思想:命令職責(zé)查詢分離,不單單指代碼,同樣也是適用于服務(wù)。

092340fe-501e-11ed-a3b6-dac502259ad0.png

優(yōu)先拆分讀還是優(yōu)先拆分寫

建議從拆分讀開始,因?yàn)樽x服務(wù)相對(duì)于寫服務(wù)簡(jiǎn)單一些,而且更容易提高系統(tǒng)對(duì)外服務(wù)的穩(wěn)定性,寫服務(wù)的流程相對(duì)底層改動(dòng)比較大,測(cè)試的周期也會(huì)比較長(zhǎng)。在前期,動(dòng)寫服務(wù)系統(tǒng)出問題的概率會(huì)比較大,所以綜合穩(wěn)定性、擴(kuò)展性來說,優(yōu)先拆分讀服務(wù)是一個(gè)比較好的選擇。

CQRS 的思想適合所有業(yè)務(wù)場(chǎng)景嗎?

以庫存系統(tǒng)舉例,我們就按照 CQRS 的思想復(fù)刻一版,看看會(huì)出現(xiàn)什么問題。

  • 每一次修改同步庫存寫入任務(wù)表
  • schedule 任務(wù)讀取任務(wù)表
  • 把任務(wù)表的修改數(shù)據(jù)同步到 Read 服務(wù)中的 Redis 中

在這個(gè)過程中,存在兩個(gè)問題:

  • 大數(shù)據(jù)量任務(wù)同步的問題:也就是 Event Bus 同步 Redis 的數(shù)據(jù)同步速度問題。

  • 延遲問題:庫存要求實(shí)時(shí)性非常高,如果因?yàn)槿蝿?wù)積壓導(dǎo)致的延遲,會(huì)讓庫存陷入困境之中。大量的庫存數(shù)量不對(duì)導(dǎo)致的超賣、超賣會(huì)瞬間擊潰業(yè)務(wù)。

094d025e-501e-11ed-a3b6-dac502259ad0.png

所以每一個(gè)架構(gòu)、每一種思想都是要結(jié)合業(yè)務(wù)去分析。我們可以借鑒 CQRS 的命令查詢職責(zé)分離,在面對(duì)業(yè)務(wù)系統(tǒng)部署的時(shí)候,不要死板的遵循固有的模式,要對(duì)現(xiàn)有的風(fēng)格做出一定的取舍。所以,我們?cè)趹?yīng)對(duì)庫存業(yè)務(wù)的時(shí)候,基于 CQRS 的風(fēng)格創(chuàng)建出了庫存獨(dú)有的 CQRS-StockCenter。

CQRS 的活學(xué)活用:CQRS-StockCenter

  • business 業(yè)務(wù)層寫入命令
  • writeService 服務(wù)寫入讀服務(wù) Redis
  • MQ 消息作為異步數(shù)據(jù)補(bǔ)全寫入 MySQL 備份、寫入流水
09921a60-501e-11ed-a3b6-dac502259ad0.png

庫存通過這套設(shè)計(jì)強(qiáng)依賴了 Redis 來作為庫存查詢、修改的中間件。保障了數(shù)據(jù)的強(qiáng)一致性。庫存在原有的服務(wù)上,分離了讀寫,保障了系統(tǒng)的 CQRS 命令職責(zé)查詢分離。

0a01606e-501e-11ed-a3b6-dac502259ad0.png

分布式事務(wù)

大家都知道事務(wù)。簡(jiǎn)單來說,事務(wù)由一組關(guān)聯(lián)操作構(gòu)成,A->B->C ,如果執(zhí)行到C報(bào)錯(cuò)了,那么要回滾 B->A。

對(duì)于本地事務(wù)來說,這個(gè)相對(duì)很簡(jiǎn)單,如果你用了事務(wù)型數(shù)據(jù)庫比如 MySQL,并且不涉及多個(gè)數(shù)據(jù)源的情況下,保障事務(wù)的 ACID 非常的容易。但是我們這里要提到的就是分布式事務(wù)。

系統(tǒng)拆分后,由于每個(gè)服務(wù)是一個(gè)獨(dú)立的模塊,負(fù)責(zé)一塊業(yè)務(wù),那么在整個(gè)業(yè)務(wù)軸的流程下,各個(gè)服務(wù)節(jié)點(diǎn)的跨系統(tǒng)事務(wù)回滾成為了一個(gè)難題。

業(yè)界也有一些方案,比如 JTA(Java Transaction API 即 Java 事務(wù) API)和 JTS(Java Transaction Service 即 Java 事務(wù)服務(wù)),為 J2EE 平臺(tái)提供了分布式事務(wù)服務(wù)。

但是,這種需要滿足 XA(兩階段提交)的標(biāo)準(zhǔn)非常的重。而且現(xiàn)在的業(yè)務(wù)多樣性,很多數(shù)據(jù)庫比如 MongoDB,并不支持 XA 的標(biāo)準(zhǔn)分布式事務(wù),一些流行的中間件,比如 RabbitMQ 和 Kafka 也不支持分布式事務(wù)。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2628

    瀏覽量

    47217
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4697

    瀏覽量

    68093
  • 架構(gòu)
    +關(guān)注

    關(guān)注

    1

    文章

    505

    瀏覽量

    25422

原文標(biāo)題:?jiǎn)误w架構(gòu)服務(wù)轉(zhuǎn)型至分布式的踩坑經(jīng)歷

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    解析一體式IO與分布式IO:從架構(gòu)到應(yīng)用

    在工業(yè)自動(dòng)化領(lǐng)域,IO(輸入/輸出)系統(tǒng)扮演著舉足輕重的角色。它們不僅負(fù)責(zé)數(shù)據(jù)的采集和控制指令的發(fā)送,還直接影響到系統(tǒng)的靈活性、可靠性和成本效益。明達(dá)技術(shù)將為您介紹一體式IO和分布式IO在架構(gòu)及應(yīng)用層的主要區(qū)別,幫助您更好地理解這兩種技術(shù)。
    的頭像 發(fā)表于 10-08 10:02 ?138次閱讀
    解析一體式IO與<b class='flag-5'>分布式</b>IO:從<b class='flag-5'>架構(gòu)</b>到應(yīng)用

    一體式IO與分布式IO:工業(yè)控制系統(tǒng)的兩種架構(gòu)

    一體式IO與分布式IO架構(gòu)各有優(yōu)勢(shì)和局限性。選擇合適的IO架構(gòu)需要根據(jù)實(shí)際的生產(chǎn)需求、系統(tǒng)規(guī)模、成本預(yù)算和維護(hù)能力綜合考慮。隨著工業(yè)自動(dòng)化技術(shù)的發(fā)展,分布式IO
    的頭像 發(fā)表于 07-17 16:12 ?647次閱讀
    一體式IO與<b class='flag-5'>分布式</b>IO:工業(yè)控制系統(tǒng)的兩種<b class='flag-5'>架構(gòu)</b>

    Redis是怎么從單體架構(gòu)發(fā)展到分布式緩存的?

    Redis 架構(gòu)是如何一步一步發(fā)展到今天的樣子的?
    的頭像 發(fā)表于 04-20 15:37 ?718次閱讀
    Redis是怎么從<b class='flag-5'>單體</b><b class='flag-5'>架構(gòu)</b>發(fā)展到<b class='flag-5'>分布式</b>緩存的?

    分布式KVM坐席管理系統(tǒng):打造智慧化城市運(yùn)營(yíng)新引擎

    訊維分布式KVM坐席管理系統(tǒng),以其獨(dú)特的分布式架構(gòu)和智能化管理特性,正在成為智慧化城市運(yùn)營(yíng)的新引擎。該系統(tǒng)通過高效的信息共享和處理,不僅提升了城市管理的工作效率,更為城市的智能化、精細(xì)化運(yùn)營(yíng)提供了
    的頭像 發(fā)表于 03-18 16:15 ?503次閱讀

    鴻蒙OS 分布式任務(wù)調(diào)度

    形式、數(shù)據(jù)結(jié)構(gòu)、服務(wù)描述語言,屏蔽硬件差異;支持遠(yuǎn)程啟動(dòng)、遠(yuǎn)程調(diào)用、業(yè)務(wù)無縫遷移等分布式任務(wù)。 分布式任務(wù)調(diào)度平臺(tái)在底層實(shí)現(xiàn) Ability(分布式任務(wù)調(diào)度的基本組件)跨設(shè)備的啟動(dòng)/關(guān)閉、連接及斷開連接以及遷移等能力,實(shí)現(xiàn)跨設(shè)備
    的頭像 發(fā)表于 01-29 16:50 ?423次閱讀

    分布式大屏控制系統(tǒng)的可擴(kuò)展性設(shè)計(jì)

    系統(tǒng)易于擴(kuò)展,可以根據(jù)需求增加或減少模塊,實(shí)現(xiàn)系統(tǒng)的靈活配置。模塊化設(shè)計(jì)還便于系統(tǒng)的維護(hù)和升級(jí),提高了系統(tǒng)的可維護(hù)性。 分布式架構(gòu):采用分布式架構(gòu),將系統(tǒng)節(jié)點(diǎn)分散在各個(gè)地理位置或網(wǎng)絡(luò)中
    的頭像 發(fā)表于 01-29 14:46 ?488次閱讀

    什么是分布式架構(gòu)?

    分布式架構(gòu)是指將一個(gè)系統(tǒng)或應(yīng)用拆分成多個(gè)獨(dú)立的節(jié)點(diǎn),這些節(jié)點(diǎn)通過網(wǎng)絡(luò)連接進(jìn)行通信和協(xié)作,以實(shí)現(xiàn)共同完成任務(wù)的一種架構(gòu)模式。這種架構(gòu)模式旨在提高系統(tǒng)的可擴(kuò)展性、可靠性和性能表現(xiàn)。 一、
    的頭像 發(fā)表于 01-12 15:04 ?1081次閱讀
    什么是<b class='flag-5'>分布式</b><b class='flag-5'>架構(gòu)</b>?

    分布式節(jié)點(diǎn)服務(wù)器是什么?

    分布式節(jié)點(diǎn)服務(wù)器是一種將多個(gè)服務(wù)器分布式連接、協(xié)同工作,以實(shí)現(xiàn)負(fù)載均衡、提高系統(tǒng)性能和可靠性、提供高可用性的服務(wù)器架構(gòu)。 具體來說,分布式節(jié)點(diǎn)服務(wù)器是通過將一個(gè)大型系統(tǒng)分成多個(gè)子系統(tǒng),
    的頭像 發(fā)表于 01-12 15:04 ?648次閱讀
    <b class='flag-5'>分布式</b>節(jié)點(diǎn)服務(wù)器是什么?

    【Vsan數(shù)據(jù)恢復(fù)】Vsan分布式存儲(chǔ)虛擬磁盤文件丟失的數(shù)據(jù)恢復(fù)案例

    一套vsan分布式存儲(chǔ)架構(gòu)有數(shù)臺(tái)服務(wù)器節(jié)點(diǎn),該vsan分布式存儲(chǔ)架構(gòu)配置了固態(tài)硬盤和機(jī)械硬盤,固態(tài)硬盤作為緩存盤使用,機(jī)械硬盤作為容量盤使用。 機(jī)房供電異常導(dǎo)致服務(wù)器異常關(guān)機(jī),工作人
    的頭像 發(fā)表于 01-03 16:39 ?558次閱讀

    分布式系統(tǒng)硬件資源池原理和接入實(shí)踐

    把各個(gè)設(shè)備的硬件外設(shè)抽象為外設(shè)信息單元,外設(shè)信息在各個(gè)可信設(shè)備之間自動(dòng)同步,如此,實(shí)現(xiàn)了外設(shè)信息的全局可見;結(jié)合分布式硬件虛擬化技術(shù),實(shí)現(xiàn)任意設(shè)備之間的硬件外設(shè)能力跨設(shè)備調(diào)用;分布式硬件資源池作為系統(tǒng)
    發(fā)表于 12-06 10:02

    分布式全閃架構(gòu)的演進(jìn)之路

    無共享體系結(jié)構(gòu) (SNA:Shared-Nothing Architecture) 是一種分布式計(jì)算體系結(jié)構(gòu),其中每個(gè)更新請(qǐng)求都由計(jì)算機(jī)群集中的單個(gè)節(jié)點(diǎn)(處理器/內(nèi)存/存儲(chǔ)單元)滿足。目的是消除節(jié)點(diǎn)
    的頭像 發(fā)表于 11-27 10:12 ?669次閱讀
    <b class='flag-5'>分布式</b>全閃<b class='flag-5'>架構(gòu)</b>的演進(jìn)之路

    springcloud分布式事務(wù)解決方案

    Spring Cloud是一套用于構(gòu)建分布式系統(tǒng)的開源框架,它提供了一系列組件和工具,可以幫助開發(fā)人員快速構(gòu)建和管理基于微服務(wù)架構(gòu)的應(yīng)用程序。在分布式系統(tǒng)中,事務(wù)的處理是一個(gè)重要的問題,因?yàn)樵?/div>
    的頭像 發(fā)表于 11-16 11:03 ?1951次閱讀

    springclould分布式教程

    的基本概念、主要組件以及如何使用Spring Cloud構(gòu)建分布式系統(tǒng)。 一、Spring Cloud的基本概念 分布式系統(tǒng) 分布式系統(tǒng)是由多個(gè)獨(dú)立計(jì)算機(jī)集合而成的系統(tǒng),這些計(jì)算機(jī)通過網(wǎng)絡(luò)進(jìn)行通信和協(xié)作,共同完成系統(tǒng)的任務(wù)。 微
    的頭像 發(fā)表于 11-16 10:59 ?426次閱讀

    spring分布式框架有哪些

    的Spring分布式框架。 Spring Cloud Spring Cloud是基于Spring Boot的分布式開發(fā)工具包。它提供了多個(gè)子項(xiàng)目,包括服務(wù)注冊(cè)與發(fā)現(xiàn)、客戶端負(fù)載均衡、斷路器、網(wǎng)關(guān)等。Spring Cloud可以幫助開發(fā)人員快速構(gòu)建基于微服務(wù)
    的頭像 發(fā)表于 11-16 10:58 ?711次閱讀

    基于EA的企業(yè)數(shù)字化轉(zhuǎn)型架構(gòu)的設(shè)計(jì)

    從數(shù)字化轉(zhuǎn)型的定義上看,至少包含兩個(gè)方面:一方面是商業(yè)模式的轉(zhuǎn)型,一方面是技術(shù)的升級(jí)。因此,數(shù)字化轉(zhuǎn)型架構(gòu)是由業(yè)務(wù)架構(gòu)、技術(shù)
    的頭像 發(fā)表于 10-29 14:51 ?1227次閱讀
    基于EA的企業(yè)數(shù)字化<b class='flag-5'>轉(zhuǎn)型</b><b class='flag-5'>架構(gòu)</b>的設(shè)計(jì)