<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          微服務面臨的分布式問題大串燒 !!

          共 4771字,需瀏覽 10分鐘

           ·

          2021-02-05 08:10


          轉自:陳珙
          cnblogs.com/skychen1218/p/14346459.html

          前言


          不少小伙伴看了我的博客的后跟我探討問題時都離不開數(shù)據(jù)一致性、數(shù)據(jù)關聯(lián)、數(shù)據(jù)重復創(chuàng)建的問題,只要大家做的分布式系統(tǒng)無論是否微服務化,或多或少都會遇到上述問題,而上述的問題的本質其實就是分布式事務、分布式數(shù)據(jù)關聯(lián)與冪等性。


          這三個問題也是很多面試官在面試的時候檢驗應聘者是否有實踐過分布式系統(tǒng)的經驗的標準之一,而微服務作為分布式系統(tǒng)的架構風格,在實施過程中也無法幸免以上問題。

          源碼:https://github.com/SkyChenSky/Sikiro


          分布式基礎概念


          用微服務架構風格設計出來的系統(tǒng)是典型的分布式系統(tǒng)。


          分布式計算是指系統(tǒng)的工作方式,主要分為數(shù)據(jù)分布式和任務分布式:


          • 數(shù)據(jù)分布式也稱為數(shù)據(jù)并行,把數(shù)據(jù)拆分后,利用多臺計算機并行執(zhí)行多個相同任務。優(yōu)點是縮短所有任務總體執(zhí)行時間,缺點是無法減少單個任務的執(zhí)行時間。


          • 任務分布式也稱為任務并行,單個串行的任務拆分成多個可并行子任務。優(yōu)點是提高性能、可擴展性、可維護性,缺點是增加設計復雜性。




          分布式系統(tǒng)必須面臨的哪些問題?


          我們日常工作的時候 ,接觸到任務分布式的情況相對比較多例如:第三方支付請求,API編排數(shù)據(jù)關聯(lián)。從場景劃分主要分為單服務多數(shù)據(jù)庫,多服務多數(shù)據(jù)庫,多服務單數(shù)據(jù)庫,以上三種場景都會存在多臺服務器之間跨網(wǎng)絡調用的情況,由原單進程單數(shù)據(jù)庫內的簡單實現(xiàn)的原子性、一致性變得不得不去面對因為跨網(wǎng)絡請求得冪等性和數(shù)據(jù)一致性。


          數(shù)據(jù)庫一致性又分讀和寫,讀對應著數(shù)據(jù)庫跨庫跨服務器的數(shù)據(jù)關聯(lián),寫對應著分布式事務的數(shù)據(jù)最終一致性的處理。


          數(shù)據(jù)關聯(lián)的復雜度場景主要體現(xiàn)在分庫分服務器與多接口數(shù)據(jù)關聯(lián)的場景應該怎么解決?


          分布式事務如果在單服務多數(shù)據(jù)庫的場景下想必大家都會想出像Sql Sever的MSDTC的XA協(xié)議事務。如果是在多服務多數(shù)據(jù)庫該選用怎樣的分布式事務方案?


          在分布式場景下冪等性的保證是無法避免的,網(wǎng)絡是存在不確定性的,一個請求可能會成功,但也會因為客觀因素導致失敗,那么重新發(fā)起請求就無發(fā)避免的了,那么如何保證我不會重復創(chuàng)建數(shù)據(jù)與數(shù)據(jù)被覆蓋呢?


          下文我將從數(shù)據(jù)關聯(lián),分布式事務和冪等性三個角度進行敘述方案。


          數(shù)據(jù)關聯(lián)


          數(shù)據(jù)關聯(lián)的主要方案有三種,應用層數(shù)據(jù)聚合、冗余設計(反范式)、數(shù)據(jù)庫從庫集成。



          舉個常見的例子:分布式情況下,比如現(xiàn)在有兩個服務,分別是用戶,訂單。每個服務都是自己獨立的數(shù)據(jù)庫。用戶數(shù)據(jù)庫有用戶信息表,訂單數(shù)據(jù)都有關聯(lián)用戶的唯一id。



          應用層數(shù)據(jù)聚合


          先調用訂單服務得到訂單列表后,再根據(jù)訂單列表的用戶ID集合調一次用戶服務查詢出用戶列表。再通過內存遍歷把訂單列表與用戶列表在業(yè)務層整合。


          優(yōu)點,實現(xiàn)簡單;缺點,也是簡單,該方案只能適合簡單的查詢過濾,以主表為驅動的關聯(lián)。

          public async Task> GetOrder()
          {
          //訂單集合
          var orderList = await _order.GetList();

          //userId集合
          var userIds = orderList.Select(a => a.UserId).ToList();

          //關聯(lián)用戶集合
          var users = await _user.GetByIds(userIds);

          //應用層數(shù)據(jù)聚合關聯(lián)
          orderList.ForEach(order =>
          {
          order.Name = users.FirstOrDefault(a => a.UserId == order.UserId)?.Name;
          });
          return orderList;
          }



          冗余設計(反范式)


          在訂單表增加和用戶有關信息的字段。


          優(yōu)點,實現(xiàn)簡單,以應用層數(shù)據(jù)聚合方案有更多的過濾條件;缺點,冗余的字段如果更新存在同步問題,該方案適用于更新頻繁少的遞增日志類數(shù)據(jù)。



          數(shù)據(jù)庫從庫集成


          通過主從同步技術,把相關的業(yè)務表同步到同一臺服務器我們稱為ReportDB,再通過在代碼層面把數(shù)據(jù)源連接指向從庫做跨庫聯(lián)表查詢處理。


          優(yōu)點,通過強大的SQL解決復雜的報表類查詢;缺點,擁有技術復雜度,需要數(shù)據(jù)庫主從處理。



          分布式事務


          分布式事務分剛性事務與柔性事務,剛性事務對應ACID理論,而柔性事務也就是最終一致性,對應BASE理論。最終一致性指如果數(shù)據(jù)再一段時間內沒有被另外的數(shù)據(jù)操作所更改,那它最終會達到與強一致性過程相同的結果。


          分布式系統(tǒng)場景下很少使用xa事務,主要原因是xa事務是基于基礎設施層面的強一致性事務,場景主要在一個服務多個數(shù)據(jù)源,追求強一致性,復雜度高,吞吐量低。


          而最終一致性方案更多是基于服務應用層的弱一致性事務,場景主要是多服務多數(shù)據(jù)源與多服務單數(shù)據(jù)源,滿足了BASE理論的三個特點:基本可用、軟狀態(tài)、最終一致性


          以訂單支付為例講述下BASE理論,客戶在A平臺發(fā)起了訂單支付,訂單支付時狀態(tài)為支付中,完成后支付后,等待支付系統(tǒng)的回調,但是這個時候,A平臺的回調API接口異常了,訂單狀態(tài)無法同步為已支付狀態(tài),這個時候客戶看到訂單的金額支付出去了,但是去搜索訂單模塊的時候發(fā)現(xiàn)還是未支付,于是反饋給了客服,開發(fā)部經過一段時間的問題定位與排查,發(fā)現(xiàn)是回調API掛了于是重啟后,數(shù)分鐘訂單狀態(tài)就同步成已完成了。



          從上面的例子來看,支付中就是軟狀態(tài),回調API服務雖然掛了,但是前臺系統(tǒng)還是可以提供給客戶端查詢使用就是基本可用,只不過訂單狀態(tài)不對,當然最后服務也恢復后達成數(shù)據(jù)最終一致性。



          分布式事務方案常見的主要有這幾種:異步請求/回調、TCC、基于消息可靠的最終一致性,TCC與基于消息可靠的最終一致性在Java和.Net都是有現(xiàn)成的框架,而異步請求/回調更多是與支付機構對接的場景會比較多,實現(xiàn)簡單、通用性強,如果團隊技術能力不足也可以使用該方案代替。


          異步請求/回調更多是應對并發(fā)處理的異步解決方案,查過相關資料并沒有納入相關分布式事務方案中,但是在我的實際工作經驗中該方案也是可以達成最終一致性。


          異步請求/回調



          該方案在與支付機構對接的場景比較常見,其核心以業(yè)務發(fā)起請求,被調用端以數(shù)據(jù)優(yōu)先入庫,稍后異步處理,處理完成后則回調請求業(yè)務端提供的API。


          這種異步處理方式一般獲取結果的方式推拉結合,外部系統(tǒng)主動回調給本地稱之為推,本地系統(tǒng)每隔一段時間主動查詢外部系統(tǒng)結果稱為拉,兩者可以按照業(yè)務的時效性結合策略使用。


          公司內部系統(tǒng)之間也可以這么做,業(yè)務系統(tǒng)請求對接系統(tǒng),被請求后數(shù)據(jù)庫直接入庫,然后通過定時調度任務異步做業(yè)務處理,業(yè)務處理成功還是失敗都修改狀態(tài),最后由回調調度任務把業(yè)務處理的狀態(tài)、處理信息回調給業(yè)務系統(tǒng)的回調API,為了避免回調調度任務因故障無法回調,可以設置策略由業(yè)務系統(tǒng)主動查詢對接系統(tǒng)提供的查詢API,推拉結合保證了系統(tǒng)可用性和數(shù)據(jù)時效性。


          TCC



          TCC是Try、Comfirm、Cancel三個單詞的縮寫,Try是資源預留、鎖定,Comfirm是確認提交,Cancel是指撤銷。一個資源的處理需要提供三個接口,從業(yè)務侵入性來看是比較強的。


          TCC的執(zhí)行步驟與2PC有點相似,先進入預提交階段,對A、B、C三個資源的分別進行try處理,如果try請求成功,相應的資源就會被修改成中間狀態(tài),可以理解成被凍結。接下來就會根據(jù)每個資源try后的情況判斷如何執(zhí)行。如果全部try成功,則會進入Comfirm處理,只要能try成功就能Comfirm成功。如果其中一個資源try失敗了,則會對所有進行Cancel處理。


          TCC與2PC看起來相似,但還是有區(qū)別的,TCC是應用服務層面的,而2PC則是基礎設施層,而2PC因為是強一致性基于遵守ACID,在事務未提交時處于阻塞狀態(tài),如果失敗則會事務回滾,而TCC是沒有事務回滾的,每個階段處理都穿透到數(shù)據(jù)庫都是Commit操作。


          基于消息的最終一致性



          該方案其實是ebay多年前提出的本地消息表的解決方案,該方案的核心點在于,執(zhí)行本地事務后再提交隊列消息,這兩步驟操作因為非原子性的跨進程操作,因為需要保證發(fā)送到消息隊列的消息能正常發(fā)布與正常的消費,這就是我們常說的保證消息可靠,那么在執(zhí)行本地事務的時候,本地業(yè)務表與消息憑據(jù)表會作為一個原子性事務提交到數(shù)據(jù)庫,消息憑據(jù)表會記錄著消息隊列的消息序列化數(shù)據(jù),如果本地事務提交成功了,但是發(fā)送消息隊列的時候失敗了,就會通過后臺線程(進程)查詢消息憑據(jù)表,把未發(fā)送成功的消息反序列化出來重新發(fā)起。


          無論再消息發(fā)布端還是消息消費端都會因為與消息隊列交互后,修改消息憑據(jù)表狀態(tài)的情況,如果與消息隊列交互是正常的,但是修改消息憑據(jù)狀態(tài)失敗了,補償服務仍然會進行不必要的重發(fā),那么這個場景容易導致數(shù)據(jù)重復創(chuàng)建與覆蓋,因此需要關注冪等性的處理了。


          該方案在.Net有CAP這個分布式事務框架,無需開發(fā)人員自己自己實現(xiàn)。


          冪等性


          冪等性的定義,相同的參數(shù)在同一個方法里,無論執(zhí)行一次還是多次都會響應相同的結果


          舉個例子銀行轉行,A銀行賬戶扣了100元,B銀行賬戶加100元,這樣數(shù)據(jù)一致的。但是在給B賬戶加100元的時候,B銀行系統(tǒng)處理超時,但是其實這個時候B銀行是已經處理成功了,只不過沒響應回去,那么A銀行系統(tǒng)就會重發(fā),如果沒有冪等性處理的話,A重試了3次,B賬戶就會加3次100。一邊扣100,一邊加300,那么數(shù)據(jù)就不一致了。


          對于查詢和刪除數(shù)據(jù)的場景都有天然的冪等性,那么我們考慮冪等性處理更多是關注于新建數(shù)據(jù)與更新數(shù)據(jù)。


          新建數(shù)據(jù)的場景,如果沒有處理好冪等性,那么就會導致數(shù)據(jù)重復創(chuàng)建,原因有可能是用戶連續(xù)點擊后發(fā)起請求,也有可能是API網(wǎng)關的retry請求。解決方案也相對比較簡單,API提供主鍵參數(shù)(流水號)傳入,就是由調用端預生成主鍵(流水號)傳入API進行請求,API端生成流水與余額扣減作為同一個事務處理。此時如果因為某個原因進行了兩次調用,因為第一次創(chuàng)建成功了,第二次則會因為主鍵的唯一性拋出了異常,這里需要注意的是得捕獲到的唯一鍵異常應處理成執(zhí)行成功的響應。


          更新數(shù)據(jù)的場景,如果沒處理號冪等性,可能會因為RPC框架或者API網(wǎng)關的Retry機制導致重復請求,這樣就會造成了ABA的數(shù)據(jù)覆蓋問題,所謂的ABA就是,第一次請求A數(shù)據(jù)已經進行寫處理了,接著到了第二次請求B數(shù)據(jù)進行對A數(shù)據(jù)進行了修改成功了,但是因為第一次請求因為某個原因導致客戶端無法接收到響應,因此API網(wǎng)關或者RPC框架進行了重發(fā),所以第三次把A數(shù)據(jù)又對已有的B數(shù)據(jù)進行修改覆蓋。針對該問題解決方案主要是使用數(shù)據(jù)版本判斷。



          以上兩種方法處理方式從數(shù)據(jù)庫層面解決,相對比較簡單直接,侵入性比較強,還有一種方案可以從Web框架層面解決,結合Web框架的AOP與Redis判斷,每次請求都會附帶一個requestID傳入到接口,由Filter攔截后Add到Redis。此方案需要引入Redis,從實現(xiàn)上比前面兩個相對復雜,但是通用性相對高一些。


          結束


          該篇到這里就結束了,主要總結了平常在分布式系統(tǒng)不得不去面對的問題,雖然大家會通過一些設計,盡可能去避免,但是唯一不變的是需求的變化,因此我們盡可能優(yōu)先了解各種處理方案,如有遇到就可針對場景選擇合適的方案。



          漫畫 | 程序員的悲哀是什么?


          我從來不承認程序員的鄙視鏈,因為我用的語言居然都沒有被鄙視的資格


          程序員最最最愛壁紙大曝光!!


          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  av京东热 | 国产成人久久7777 | 国产A片免费领取 | 免费A片视频在线观看 | 亚洲日韩一区二区三区四区丨高清 |