敖丙帶你了解電商流程引擎
本文轉(zhuǎn)載自微信公眾號「三太子敖丙」,作者三太子敖丙。轉(zhuǎn)載本文請聯(lián)系三太子敖丙公眾號。
正文
在電商里面處理復(fù)雜的業(yè)務(wù)邏輯場景很多,我們還是以創(chuàng)建商品為列子。很多人可能會問創(chuàng)建商品很復(fù)雜嗎?我們接著往下看就知道了。
創(chuàng)建商品流程:
- 參數(shù)必填性校驗(yàn)
- 參數(shù)數(shù)據(jù)轉(zhuǎn)換
- 商品基礎(chǔ)信息校驗(yàn)
- 商品與商家之間的校驗(yàn)
- 類目信息校驗(yàn)
- 商品交易信息校驗(yàn)(這個(gè)看公司業(yè)務(wù)決定)
- SKU相關(guān)信息校驗(yàn)
- 商品是否需要有特定標(biāo)簽校驗(yàn)(看公司業(yè)務(wù)決定)
- 商品類型校驗(yàn)(普通,卡片,視頻.......)
- 商品風(fēng)控校驗(yàn)
- 保存商品信息
- 保存SKU信息
- 保存商品詳情信息
- 重量模版
- 運(yùn)費(fèi)
- 配送區(qū)域
- 。。。。。。。
大家現(xiàn)在看看覺得創(chuàng)建一個(gè)上還簡單嗎?這里商品類型里面還涉及到各個(gè)業(yè)務(wù)場景校驗(yàn),我們就先不談了。針對這樣的情況我們應(yīng)該怎么去寫這個(gè)代碼呢?我翻看了一下以前大學(xué)寫的一些代碼整體的代碼格式大概也就是這個(gè)樣子
這么寫其實(shí)也沒有什么問題,功能也能實(shí)現(xiàn)。但是這么寫其實(shí)有很多弊端的:
- 代碼可讀性不高
- 代碼擴(kuò)展性不高
- 耦合性太強(qiáng),有些東西不好公用
- (重點(diǎn))整體創(chuàng)建執(zhí)行流程時(shí)間太長,串行調(diào)用下游服務(wù)
看到這樣代碼我們首先都是吐槽一頓,然后還是老老實(shí)實(shí)是去改。有動(dòng)手能力強(qiáng)的同學(xué)可能會想著去優(yōu)化一下。再看看這個(gè)流程,其實(shí)在創(chuàng)建商品的時(shí)候我們很多校驗(yàn)和保存數(shù)據(jù)是m沒有依賴且互不影響的,我們完全可以去并行執(zhí)行。
節(jié)省創(chuàng)建商品的流程時(shí)間,提高用戶體驗(yàn)。(PS:就好比兩條高速一條堵車,一條暢通無阻,選哪條?)所以針對這么并行問題我還是給大家畫了一個(gè)流程圖:
通過這圖我們看到創(chuàng)建一個(gè)商品流程,我們調(diào)用的下游服務(wù)可能是有十幾二十個(gè)或者更多,假設(shè)我們一次RPC調(diào)用的平均返回時(shí)間是50毫秒,串行執(zhí)行時(shí)間可能就到1-2秒了,那么我們并行執(zhí)行話也就200到300毫秒了。
所以本著這種思想可能有人會問,為什么我不能異步,不能用消息?創(chuàng)建商品假設(shè)用異步消息的話,如果消費(fèi)失敗那用戶創(chuàng)建的商品成功保存其他信息失敗了,那對用戶來說不是更加體驗(yàn)不好了?
說了這么多我們開始擼代碼了
先創(chuàng)建一個(gè)Context 上下文,作為我們的調(diào)用下游服務(wù)的返回結(jié)果
圖片
第二步創(chuàng)建我們的流程節(jié)點(diǎn),這相當(dāng)于就是保存我們整個(gè)流程中需要執(zhí)行下游服務(wù)的節(jié)點(diǎn),以Map作為保存數(shù)據(jù),NodeConf 節(jié)點(diǎn)設(shè)置參數(shù),自定義請求服務(wù)超時(shí)時(shí)間(因?yàn)椴⑿形覀兪怯玫木€程池或者通過get設(shè)置時(shí)間get返回值結(jié)果)
第三步引擎類,這個(gè)也是我們的核心類。通過我們添加的node節(jié)點(diǎn)判斷我們哪些流程是需要串行的那些是需要并行的,通過線程池創(chuàng)建線程放入Feature中,來達(dá)到同步執(zhí)行的效果。
在使用線程池的時(shí)候我們需要考慮不要設(shè)置的參數(shù)過大,開啟另外的線程也是會占用機(jī)器內(nèi)脆的,一個(gè)線程按1兆來算,你開啟幾百上千個(gè),也會占用很大的一部分內(nèi)存。盡可能的去采用池化思想,這里就按大家實(shí)際場景去做測試。
第四步執(zhí)行Call方法,也就是執(zhí)行我們的node節(jié)點(diǎn)。
第5步創(chuàng)建節(jié)點(diǎn)接口,這里我們要定義一個(gè)ResultKey,這個(gè)Key也就是跟我流程中的這個(gè)節(jié)點(diǎn)所綁定,在獲取數(shù)據(jù)的時(shí)候也就是通過者key來標(biāo)識
第6步因?yàn)槲覀冊诠?jié)點(diǎn)里面存的Class類,所以我們得通過實(shí)現(xiàn)ApplicationContextAware類來獲取Spring容器中的bean實(shí)例
第7步那就是來創(chuàng)建兩個(gè)測試node節(jié)點(diǎn)
最后當(dāng)然就是我們的測試結(jié)果啦,這里我們創(chuàng)建兩個(gè)節(jié)點(diǎn)NodeOne 和NodeTwo 作為模擬真實(shí)業(yè)務(wù)場景的節(jié)點(diǎn),通過一個(gè)后面的three作為一個(gè)group 需要并行執(zhí)行的節(jié)點(diǎn)。
看完代碼最后再給大家來一個(gè)總體的流程圖吧
看完是不是覺得感覺自己頓悟,以后再面對復(fù)雜流程的業(yè)務(wù)也就OK拿下了。
思考
其實(shí)這里還有很多優(yōu)化點(diǎn),每個(gè)人遇到復(fù)雜的場景可能也不一樣,只能說給大家提供一個(gè)思想吧,針對不同的場景大家再去做改造吧!!!
給大家做一個(gè)擴(kuò)展:
細(xì)心的同學(xué)可能會發(fā)現(xiàn)這都是強(qiáng)依賴性,能不能有弱依賴在里面呢?
答案:當(dāng)然可以有弱依賴了,在 FlowNode.NodeConf中我們既然可以設(shè)置超時(shí)時(shí)間 我們也可以在添加一個(gè)參數(shù)來確定是都是弱依賴。在我們的future.get獲取結(jié)果的時(shí)候當(dāng)出現(xiàn)異??梢詂atch住,強(qiáng)依賴則終止流程返回錯(cuò)誤信息,否則記錄錯(cuò)誤日志,流程continue
我們流程保存現(xiàn)在是用的靜態(tài)代碼塊,可不可以換其他的方式保存節(jié)點(diǎn)呢?
答案:這個(gè)當(dāng)然也可以,我們保存在數(shù)據(jù)庫,ACM,Apollo等等都是可以的。這個(gè)取決于你們自己的業(yè)務(wù)和成本問題。因?yàn)榱鞒涛覀円话闶遣粫?jīng)常換的,所以我還是建議代碼寫死就好了
采用線程池去調(diào)用下游服務(wù),會不會造成服務(wù)鏈路追蹤失敗呢?
答案:這個(gè)不能說絕對,但是如果是保存在ThreadLocal中那肯定是會失效的,ThreadLocal中的KEY也就跟當(dāng)前線程的ID有關(guān),都開啟新的線程了,那肯定也就是丟失了
如果采用了ThreadLocal 在節(jié)點(diǎn)中是不是就失效了?
答案:第三點(diǎn)已經(jīng)給出答案了
總結(jié)
今天的復(fù)雜業(yè)務(wù)邏輯的流程引擎也就完結(jié)了,在我每次看到新的技術(shù)以及知識點(diǎn)的時(shí)候我都有一種開悟的感覺,引用 鄧寧-克魯格的心理效應(yīng)來說,在我們的這個(gè)年紀(jì)其實(shí)就是開悟之坡。后
面我會接著再為大家怒肝設(shè)計(jì)模式,完了也就會再跟大家聊聊重構(gòu)。其實(shí)也就是用我們的設(shè)計(jì)模式來優(yōu)化我們的代碼。