国产欧美日韩第一页|日本一二三不卡视频|在线精品小视频,亚洲第一免费播放区,metcn人体亚洲一区,亚洲精品午夜视频

幫助中心 >  行業(yè)資訊 >  其他 >  微服務(wù)化后,需要注意的幾點(diǎn)

微服務(wù)化后,需要注意的幾點(diǎn)

2021-05-07 10:29:41 169

隨著(zhù)業(yè)務(wù)發(fā)展,很多系統需要經(jīng)歷服務(wù)拆分的過(guò)程。微服務(wù)化過(guò)程踩坑也是很正常的事。如果在服務(wù)拆分之前做好充分準備,能幫我們少走很多彎路。本文主要從服務(wù)依賴(lài),接口版本,隔離,數據一致等方面說(shuō)說(shuō)微服務(wù)化過(guò)程應該注意的點(diǎn)。

一、循環(huán)依賴(lài)問(wèn)題

微服務(wù)化之后服務(wù)之間會(huì )存在各種依賴(lài)關(guān)系,不過(guò)依賴(lài)需要遵循一定的規則,不能太隨意。否則,就會(huì )出現循環(huán)依賴(lài)的問(wèn)題,而且會(huì )讓調用關(guān)系變得錯綜復雜難于維護。下面是服務(wù)依賴(lài)的幾條規則:

1,上層服務(wù)可以調用下層服務(wù)。

2,同級服務(wù)之間不能產(chǎn)生依賴(lài)關(guān)系,及不能產(chǎn)生調用關(guān)系。

3,下層服務(wù)不能調用上層服務(wù)。

4,服務(wù)之間的調用關(guān)系只能是單向的。

例如,在電商系統里包括支付服務(wù)(Pay),庫存服務(wù)(Inventory),訂單服務(wù)(Order)。支付服務(wù)和庫存服務(wù)屬于基礎服務(wù),訂單服務(wù)屬于上層服務(wù)。支付服務(wù)和庫存服務(wù)是同級的服務(wù),他們之間不能存在調用關(guān)系。訂單服務(wù)屬于上層服務(wù),訂單服務(wù)可以調用支付服務(wù)和庫存服務(wù),但是支付服務(wù)和庫存服務(wù)不能調用上層的訂單服務(wù)。 

假設我們不管這些規則,讓Order和Pay可以互相調用。這樣就會(huì )產(chǎn)生循環(huán)依賴(lài),Order調用Pay,Pay也調用Order,這樣彼此都會(huì )依賴(lài)對方。

循環(huán)依賴(lài)導致哪些問(wèn)題?

1,無(wú)限遞歸調用

假如,Order調用Pay的A方法,Pay調用Order的B方法。然后,A方法里又調用了Order的B方法,B方法里又調用了Pay的A方法。這樣就會(huì )產(chǎn)生無(wú)限的遞歸調用,后果自然不言而喻了。

 

image.png



image.png


2,部署依賴(lài)問(wèn)題

假設Order,Pay,Inventory彼此之間都可以通過(guò)API互相調用。當API接口發(fā)生變更時(shí),為了讓其他服務(wù)能夠正常調用,API需要重新編譯。如果Order和Pay的API都有變化,上線(xiàn)發(fā)布時(shí)就需要特別小心。為了保證發(fā)布成功,就需要根據服務(wù)間API的依賴(lài)關(guān)系,詳細考慮先打包部署哪個(gè)服務(wù),后打包部署哪個(gè)服務(wù),才不至于發(fā)布失敗。如果有更多的服務(wù)呢?比如10幾個(gè),梳理依賴(lài)關(guān)系都會(huì )把人搞瘋的。 

3,另外,循環(huán)依賴(lài)會(huì )讓服務(wù)間的調用關(guān)系變得錯綜復雜,系統難于維護。

二、接口版本兼容

一些初中級程序員往往會(huì )忽略接口變更的問(wèn)題,經(jīng)常會(huì )因為接口變更導致線(xiàn)上問(wèn)題。比如某個(gè)小型電商平臺的訂單服務(wù)調用支付服務(wù)的某個(gè)接口,產(chǎn)品突然提了一個(gè)需求,這個(gè)需求需要在這個(gè)支付接口上加一個(gè)參數。開(kāi)發(fā)這個(gè)需求的是個(gè)新手,他直接在原來(lái)的接口方法上實(shí)現了需求并加上了參數,聯(lián)調測試通過(guò)后就發(fā)布上線(xiàn)了。結果剛上線(xiàn)訂單服務(wù)就開(kāi)始報錯,因為方法變了,加了參數,訂單服務(wù)找不到老的方法了。所以就會(huì )一直報錯,直到訂單服務(wù)上線(xiàn)為止。

所以我們一定要注意接口版本問(wèn)題。我們可以新加一個(gè)方法去重載老的方法,在新方法里實(shí)現新的功能,新方法的定義除了多一個(gè)參數外,其他的和老方法一樣。也就是給老方法加了一個(gè)新版本。

這樣在支付服務(wù)上線(xiàn)后,訂單服務(wù)上線(xiàn)之前就不會(huì )報錯了,因為老方法仍然可用。訂單服務(wù)上線(xiàn)后就直接切到了新版本的方法。

如果我們服務(wù)框架選用的是Dubbo,當一個(gè)接口的實(shí)現,出現不兼容升級時(shí),可以用Dubbo的版本號過(guò)渡,版本號不同的服務(wù)相互間不引用。

可以按照以下的步驟進(jìn)行版本遷移:

1. 在低壓力時(shí)間段,先升級一半提供者為新版本

2. 再將所有消費者升級為新版本

3. 然后將剩下的一半提供者升級為新版本

老版本服務(wù)提供者配置:


image.png


新版本服務(wù)提供者配置:


image.png


老版本服務(wù)消費者配置:


image.png


新版本服務(wù)消費者配置:


image.png


三、關(guān)于隔離的考慮

1.數據隔離:

實(shí)際上,服務(wù)化的其中一個(gè)基本原則就是數據隔離,不同服務(wù)應該有自己的專(zhuān)屬數據庫,而不應該共用相同的數據庫,數據訪(fǎng)問(wèn)可以通過(guò)服務(wù)接口或者消息隊列的方式。

很多公司微服務(wù)化后,只做了代碼工程的拆分,不同服務(wù)對應的數據仍然存放在同一個(gè)數據庫中。這樣做至少存在四個(gè)問(wèn)題:

(1)數據安全問(wèn)題。別人的服務(wù)不但可以訪(fǎng)問(wèn)你的數據,而且還能修改和刪除你的數據。

(2)導致數據庫連接耗盡。一旦某個(gè)服務(wù)的開(kāi)發(fā)者寫(xiě)了一個(gè)慢SQL,并且這個(gè)服務(wù)也沒(méi)有合理限制連接數??赡軙?huì )消耗掉所有的數據庫連接,進(jìn)而造成訪(fǎng)問(wèn)相同數據庫的其他服務(wù)拿不到數據庫連接,無(wú)法訪(fǎng)問(wèn)數據庫。

(3)表關(guān)聯(lián)查詢(xún)。無(wú)法避免其他服務(wù)的開(kāi)發(fā)者,為了快速上線(xiàn)某些需求。直接查詢(xún)其他服務(wù)的表,或者跨服務(wù)做表關(guān)聯(lián)查詢(xún)。這樣會(huì )造成服務(wù)間的耦合越來(lái)越嚴重。

(4)表結構變化的影響。如果某個(gè)服務(wù)直接依賴(lài)于其他服務(wù)的數據,一旦表結構發(fā)生任何變化,比如修改表名或者字段。很可能會(huì )產(chǎn)生災難性后果。

2.部署隔離:

我們經(jīng)常會(huì )遇到秒殺業(yè)務(wù)和日常業(yè)務(wù)依賴(lài)同一個(gè)服務(wù),以及C端服務(wù)和內部運營(yíng)系統依賴(lài)同一個(gè)服務(wù)的情況,比如說(shuō)都依賴(lài)支付服務(wù)。而秒殺系統的瞬間訪(fǎng)問(wèn)量很高,可能會(huì )對服務(wù)帶來(lái)巨大的壓力,甚至壓垮服務(wù)。內部運營(yíng)系統也經(jīng)常有批量數據導出的操作,同樣會(huì )給服務(wù)帶來(lái)一定的壓力。這些都是不穩定因素。所以我們可以將這些共同依賴(lài)的服務(wù)分組部署,不同的分組服務(wù)于不同的業(yè)務(wù),避免相互干擾。


2.png


3.業(yè)務(wù)隔離:

以秒殺為例。從業(yè)務(wù)上把秒殺和日常的售賣(mài)區分開(kāi)來(lái),把秒殺做為營(yíng)銷(xiāo)活動(dòng),要參與秒殺的商品需要提前報名參加活動(dòng),這樣我們就能提前知道哪些商家哪些商品要參與秒殺,可以根據提報的商品提前生成商品詳情靜態(tài)頁(yè)面并上傳到CDN預熱,提報的商品庫存也需要提前預熱,可以將商品庫存在活動(dòng)開(kāi)始前預熱到Redis,避免秒殺開(kāi)始后大量訪(fǎng)問(wèn)穿透到數據庫。


99.jpg

       

四、數據一致性問(wèn)題

做了微服務(wù)拆分后,還可能會(huì )出現數據不一致的問(wèn)題。比如支付服務(wù)中,支付狀態(tài)發(fā)生變更后要通知訂單服務(wù)修改對應訂單的狀態(tài)。如果支付服務(wù)沒(méi)有正常通知到訂單服務(wù),或者訂單服務(wù)接到通知后沒(méi)能正常處理通知,就會(huì )導致支付服務(wù)的支付狀態(tài)和訂單服務(wù)的支付狀態(tài)不一致,也就是數據會(huì )不一致。

1.那么如何避免數據不一致的問(wèn)題產(chǎn)生呢?

我們通常所說(shuō)的服務(wù)間數據一致性,主要包括數據強一致性和最終一致性。對于強一致性,使用的業(yè)務(wù)場(chǎng)景很少,而且會(huì )有明顯的性能問(wèn)題。所以這里我們主要討論最終一致性。

一般我們可以采用如下幾種方式來(lái)保證服務(wù)間數據的最終一致:

2.定時(shí)任務(wù)重試,同步調用接口

這種方式,采用定時(shí)任務(wù)去掃表,每次定時(shí)任務(wù)掃描所有未成功的記錄,并發(fā)起重試。注意,要保證重試操作的冪等性。

這種方式的優(yōu)點(diǎn)是:實(shí)現簡(jiǎn)單。缺點(diǎn)是:需要啟動(dòng)專(zhuān)門(mén)的定時(shí)任務(wù),定時(shí)任務(wù)存在一定的時(shí)間間隔,實(shí)時(shí)性會(huì )比較差。而且同步接口調用的方式,耦合較重,有時(shí)無(wú)法避免循環(huán)依賴(lài)的問(wèn)題。

比如,Order服務(wù)可以調用Pay,Pay做為基礎服務(wù)不應該調用Order。當Pay的某筆交易狀態(tài)發(fā)生變更后,需要通知Order。如果采用定時(shí)任務(wù)的方式就需要Order提供一個(gè)接口,定時(shí)任務(wù)掃描過(guò)程中同步調用這個(gè)接口去更新Order的訂單狀態(tài)。這樣又違反了單向依賴(lài)的原則,形成了循環(huán)依賴(lài)。

異步消息隊列,發(fā)送事務(wù)型消息

 

100.jpg

 

如上圖,以電商下單流程為例。下單流程最后一步,通知WMS撿貨出庫,是異步消息走消息隊列。

 

image.png


按上面代碼,大家不難發(fā)現問(wèn)題!如果發(fā)送撿貨出庫消息失敗,數據就會(huì )不一致!有人說(shuō)我可以在代碼上加上重試邏輯和回退邏輯,發(fā)消息失敗就重發(fā),多次重試失敗所有操作都回退。這樣一來(lái)邏輯就會(huì )特別復雜,回退失敗要考慮,而且還有可能消息已經(jīng)發(fā)送成功了,但是由于網(wǎng)絡(luò )等問(wèn)題發(fā)送方?jīng)]得到MQ的響應。還有可能出現發(fā)送方宕機的情況。這些問(wèn)題都要考慮進(jìn)來(lái)!

幸好,有些消息隊列幫我們解決了這些問(wèn)題。比如阿里開(kāi)源的RocketMQ(目前已經(jīng)是Apache開(kāi)源項目),4.3.0版本開(kāi)始支持事務(wù)型消息(實(shí)際上早在貢獻給Apache之前曾經(jīng)支持過(guò)事務(wù)消息,后來(lái)被閹割了,4.3.0版本重新開(kāi)始支持事務(wù)型消息)。

先看看RocketMQ發(fā)送事務(wù)型消息的流程:


111.jpg


(1)發(fā)送半消息(所有事務(wù)型消息都要經(jīng)歷確認過(guò)程,從而確定最終提交或回滾(拋棄消息),未被確認的消息稱(chēng)為“半消息”或者“預備消息”,“待確認消息”)

(2)半消息發(fā)送成功并響應給發(fā)送方

(3)執行本地事務(wù),根據本地事務(wù)執行結果,發(fā)送提交或回滾的確認消息

(4)如果確認消息丟失(網(wǎng)絡(luò )問(wèn)題或者生產(chǎn)者故障等問(wèn)題),MQ向發(fā)送方回查執行結果

(5)根據上一步驟回查結果,確定提交或者回滾(拋棄消息)

看完事務(wù)型消息發(fā)送流程,有些讀者可能沒(méi)有完全理解,不要緊,我們來(lái)分析一下!

問(wèn)題1:假如發(fā)送方發(fā)送半消息失敗怎么辦?

半消息(待確認消息)是消息發(fā)送方發(fā)送的,如果失敗,發(fā)送方自己是知道的并可以做相應處理。

問(wèn)題2:假如發(fā)送方執行完本地事務(wù)后,發(fā)送確認消息通知MQ提交或回滾消息時(shí)失敗了(網(wǎng)絡(luò )問(wèn)題,發(fā)送方重啟等情況),怎么辦?

沒(méi)關(guān)系,當MQ發(fā)現一個(gè)消息長(cháng)時(shí)間處于半消息(待確認消息)的狀態(tài),MQ會(huì )以定時(shí)任務(wù)的方式主動(dòng)回查發(fā)送方并獲取發(fā)送方執行結果。這樣即便出現網(wǎng)絡(luò )問(wèn)題或者發(fā)送方本身的問(wèn)題(重啟,宕機等),MQ通過(guò)定時(shí)任務(wù)主動(dòng)回查發(fā)送方基本都能確認消息最終要提交還是回滾(拋棄)。當然出于性能和半消息堆積方面的考慮,MQ本身也會(huì )有回查次數的限制。

問(wèn)題3:如何保證消費一定成功呢?

RocketMQ本身有ack機制,來(lái)保證消息能夠被正常消費。如果消費失?。ㄏ⒂嗛喎匠鲥e,宕機等原因),RocketMQ會(huì )把消息重發(fā)回Broker,在某個(gè)延遲時(shí)間點(diǎn)后(默認10秒后)重新投遞消息。

結合上面幾個(gè)同步調用hmily完整代碼如下:


image.png


image.png


image.png


image.png


如果執行到TransactionListenerImpl.executeLocalTransaction方法,說(shuō)明半消息已經(jīng)發(fā)送成功了,也說(shuō)明OrderService.makePayment方法的四個(gè)步驟都執行成功了,此時(shí)tcc也到了confirm階段,所以在TransactionListenerImpl.executeLocalTransaction方法里可以直接返回LocalTransactionState.COMMIT_MESSAGE 讓 MQ提交這條消息,同時(shí)將該訂單信息和對應的消息狀態(tài)保存在共享map里,以備確認消息發(fā)送失敗時(shí)MQ回查消息狀態(tài)使用。

3.采用TCC,SAGA,Seata等框架

以上就是微服務(wù)化后需要注意的幾點(diǎn)。


、




提交成功!非常感謝您的反饋,我們會(huì )繼續努力做到更好!

這條文檔是否有幫助解決問(wèn)題?

非常抱歉未能幫助到您。為了給您提供更好的服務(wù),我們很需要您進(jìn)一步的反饋信息:

在文檔使用中是否遇到以下問(wèn)題: