精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

哪種分布式事務(wù)處理方案效率最高?必然是...

開發(fā) 架構(gòu) 分布式
在微服務(wù)系統(tǒng)中,服務(wù)之間的互相調(diào)用,我們可以使用 HTTP 的方式,例如 OpenFeign,也可以使用 RPC 的方式,例如 Dubbo,除了這些方案之外,我們也可以使用消息驅(qū)動(dòng),這是一種典型的響應(yīng)式系統(tǒng)設(shè)計(jì)方案。

[[421456]]

前面幾篇文章松哥和大家介紹了 Seata 中四種分布式事務(wù)處理方案,相信經(jīng)過前面的幾篇文章的學(xué)習(xí),大家對于 Seata 中的分布式事務(wù)已經(jīng)非常了解了。還沒看過前面文章的小伙伴,可以先看一下:

  • 五分鐘帶你體驗(yàn)一把分布式事務(wù)!so easy!
  • 看了那么多博客,還是不懂 TCC,不妨看看這個(gè)案例!
  • XA 事務(wù)水很深,小伙子我怕你把握不住!
  • 你這 Saga 事務(wù)保“隔離性”嗎?

不過很多小伙伴看完后感覺 Seata 對于分布式事務(wù)的處理,代碼雖然簡單,但是內(nèi)部花費(fèi)在網(wǎng)絡(luò)上的時(shí)間消耗太多了,在高并發(fā)場景下,這似乎并不是一種很好的解決方案。

要說哪種分布式事務(wù)處理方案效率高,必然繞不開消息中間件!基于消息中間件的兩階段提交方案,通常用在高并發(fā)場景下。這種方式通過犧牲數(shù)據(jù)的強(qiáng)一致性換取性能的大幅提升,不過實(shí)現(xiàn)這種方式的成本和復(fù)雜度是比較高的,使用時(shí)還要看實(shí)際業(yè)務(wù)情況。

今天松哥想通過一個(gè)簡單的案例,來和大家聊一聊如何通過消息中間件來處理分布式事務(wù)。

1. 思路分析

先來說說整體思路。

有一個(gè)名詞叫做消息驅(qū)動(dòng)的微服務(wù),相信很多小伙伴都聽說過。怎么理解呢?

在微服務(wù)系統(tǒng)中,服務(wù)之間的互相調(diào)用,我們可以使用 HTTP 的方式,例如 OpenFeign,也可以使用 RPC 的方式,例如 Dubbo,除了這些方案之外,我們也可以使用消息驅(qū)動(dòng),這是一種典型的響應(yīng)式系統(tǒng)設(shè)計(jì)方案。

在消息驅(qū)動(dòng)的微服務(wù)中,服務(wù)之間不再互相直接調(diào)用,當(dāng)服務(wù)之間需要通信時(shí),就把通信內(nèi)容發(fā)送到消息中間件上,另一個(gè)服務(wù)則通過監(jiān)聽消息中間件中的消息隊(duì)列,來完成相應(yīng)的業(yè)務(wù)邏輯調(diào)用,過程就是這么個(gè)過程,并不難,具體怎么玩,我們繼續(xù)往下看。

2. 業(yè)務(wù)分析

折騰了半天,后來松哥在網(wǎng)上找到了一個(gè)別人寫好的例子,我覺得用來演示這個(gè)問題特別合適,所以我就沒有自己寫案例了,直接用別人的代碼,我們來逐個(gè)分析,跟前面講分布式事務(wù) Seata 的方式一致。

首先我們來看如下一張流程圖,這是一個(gè)用戶購票的案例:

當(dāng)用戶想要購買一張票時(shí):

  1. 向新訂單隊(duì)列中寫入一條數(shù)據(jù)。
  2. Order Service 負(fù)責(zé)消費(fèi)這個(gè)隊(duì)列中的消息,完成訂單的創(chuàng)建,然后再向新訂單繳費(fèi)隊(duì)列中寫入一條消息。
  3. User Service 負(fù)責(zé)消費(fèi)新訂單繳費(fèi)隊(duì)列中的消息,在 User Service 中完成對用戶賬戶余額的劃扣,然后向新訂單轉(zhuǎn)移票隊(duì)列中寫入一條消息。
  4. Ticket Service 負(fù)責(zé)消費(fèi)新訂單轉(zhuǎn)移票隊(duì)列,在 Ticket Service 中完成票的轉(zhuǎn)移,然后發(fā)送一條消息給訂單完成隊(duì)列。
  5. 最后 Order Service 中負(fù)責(zé)監(jiān)聽訂單完成隊(duì)列,處理完成后的訂單。

這就是一個(gè)典型的消息驅(qū)動(dòng)微服務(wù),也是一個(gè)典型的響應(yīng)式系統(tǒng)。在這個(gè)系統(tǒng)中,一共有三個(gè)服務(wù),分別是:

  • Order Service
  • User Service
  • Ticket Service

這三個(gè)服務(wù)之間不會(huì)進(jìn)行任何形式的直接調(diào)用,大家有事都是直接發(fā)送到消息中間件,其他服務(wù)則從消息中間件中獲取自己想要的消息然后進(jìn)行處理。

具體到我們的實(shí)踐中,則多了一個(gè)檢查票是否夠用的流程,如下圖:

創(chuàng)建訂單時(shí),先由 Ticket 服務(wù)檢查票是否夠用,沒問題的話再繼續(xù)發(fā)起訂單的創(chuàng)建。其他過程我就不說了。

另外還需要注意,在售票系統(tǒng)中,由于每張票都不同,例如每張票可能有座位啥的,因此一張票在數(shù)據(jù)庫中往往是被設(shè)計(jì)成一條記錄。

3. 實(shí)踐

流程我已經(jīng)說明白了,接下來我們就來看看具體的代碼實(shí)踐。

3.1 準(zhǔn)備數(shù)據(jù)庫

首先我們準(zhǔn)備三個(gè)數(shù)據(jù)庫,分別是:

  • javaboy_order:訂單庫,用戶創(chuàng)建訂單等操作,在這個(gè)數(shù)據(jù)庫中完成。
  • javaboy_ticket:票務(wù)庫,這個(gè)庫中保存著所有的票據(jù)信息,每一張票都是一條記錄,都保存在這個(gè)庫中。
  • javaboy_user:用戶庫,這里保存著用戶的賬戶余額以及付款記錄等信息。

每個(gè)庫中都有各自對應(yīng)的表,為了操作方便,這些表不用自己創(chuàng)建,將來等項(xiàng)目啟動(dòng)了,利用 JPA 自動(dòng)創(chuàng)建即可。

3.2 項(xiàng)目概覽

我們先來整體上看下這個(gè)項(xiàng)目,公眾號(hào)后臺(tái)回復(fù) mq_tran 可以下載完整代碼:

一共有五個(gè)服務(wù):

  • eureka:注冊中心
  • order:訂單服務(wù)
  • service:公共模塊
  • ticket:票務(wù)服務(wù)
  • user:用戶服務(wù)

下面分別來說。

3.3 注冊中心

有人說,都消息驅(qū)動(dòng)了,還要注冊中心干嘛?

消息驅(qū)動(dòng)沒錯(cuò),消息驅(qū)動(dòng)微服務(wù)之后每個(gè)服務(wù)只管把消息往消息中間件上扔,每個(gè)服務(wù)又只管消費(fèi)消息中間件上的消息,這個(gè)時(shí)候?qū)τ诜?wù)注冊中心似乎不是那么強(qiáng)需要。不過在我們這個(gè)案例中,消息驅(qū)動(dòng)主要用來處理事務(wù)問題,其他常規(guī)需求我們還是用 OpenFeign 來處理,所以這里我們依然需要一個(gè)注冊中心。

這里的注冊中心我就選擇常見的 Eureka,省事一些。由于本文主要是和大家聊分布式事務(wù),所以涉及到微服務(wù)的東西我就簡單介紹下,不會(huì)占用過多篇幅,如果大家還不熟悉 Spring Cloud 的用法,可以在公眾號(hào)后臺(tái)回復(fù) vhr 有一套視頻介紹。

服務(wù)注冊中心的創(chuàng)建記得加上 Spring Security,將自己的服務(wù)注冊中心保護(hù)起來。

這塊有一個(gè)小小的細(xì)節(jié)和大家多說兩句。

Eureka 用 Spring Security 保護(hù)起來之后,以后其他服務(wù)注冊都是通過 Http Basic 來認(rèn)證,所以我們要在代碼中開啟 Http Basic 認(rèn)證,如下(以前舊版本不需要下面這段代碼,但是新版本需要):

  1. @Configuration 
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter { 
  3.     @Override 
  4.     protected void configure(HttpSecurity http) throws Exception { 
  5.         http.authorizeRequests() 
  6.                 .anyRequest().authenticated() 
  7.                 .and() 
  8.                 .httpBasic() 
  9.                 .and().formLogin().and().csrf().disable(); 
  10.     } 

3.4 購票服務(wù)

接下來我們就來看看購票服務(wù)。

購票是從下訂單開始,所以我們就先從訂單服務(wù) order 開始整個(gè)流程的分析。

3.4.1 新訂單處理(order)

當(dāng)用戶發(fā)起一個(gè)購票請求后,這個(gè)請求發(fā)送到 order 服務(wù)上,order 服務(wù)首先會(huì)向 order:new 隊(duì)列發(fā)送一條消息,開啟一個(gè)訂單的處理流程。代碼如下:

  1. @Transactional 
  2. @PostMapping(""
  3. public void create(@RequestBody OrderDTO dto) { 
  4.     dto.setUuid(UUID.randomUUID().toString()); 
  5.     rabbitTemplate.convertAndSend("order:new", dto); 

上面設(shè)置的 UUID 是整個(gè)訂單在處理過程中的一個(gè)唯一標(biāo)志符,也算是一條主線。

order:new 隊(duì)列中的消息將被 ticket 服務(wù)消費(fèi),ticket 服務(wù)消費(fèi) order:new 中的消息,并進(jìn)行鎖票操作(鎖票的目的防止有兩個(gè)消費(fèi)同時(shí)購買同一張票),鎖票成功后,ticket 服務(wù)將向 order:locked 隊(duì)列發(fā)送一條消息,表示鎖票成功;否則向 order:fail 隊(duì)列發(fā)送一條消息表示鎖票失敗。

這里的 OrderDTO 對象將貫穿整個(gè)購票過程。

3.4.2 鎖票(ticket)

鎖票操作是在 ticket 服務(wù)中完成的,代碼如下:

  1. @Transactional 
  2. @RabbitListener(queues = "order:new"
  3. public void handleTicketLock(OrderDTO msg) { 
  4.     LOG.info("Get new order for ticket lock:{}", msg); 
  5.     int lockCount = ticketRepository.lockTicket(msg.getCustomerId(), msg.getTicketNum()); 
  6.     if (lockCount == 0) { 
  7.         msg.setStatus("TICKET_LOCK_FAIL"); 
  8.         rabbitTemplate.convertAndSend("order:fail", msg); 
  9.     } else { 
  10.         msg.setStatus("TICKET_LOCKED"); 
  11.         rabbitTemplate.convertAndSend("order:locked", msg); 
  12.     } 

先調(diào)用 lockTicket 方法去數(shù)據(jù)庫中鎖票,所謂的鎖票就是將要購買的票的 lock_user 字段設(shè)置為 customer_id(購買者的 id)。

如果鎖票成功(即數(shù)據(jù)庫修改成功),設(shè)置 msg 的狀態(tài)為 TICKET_LOCKED,同時(shí)發(fā)送消息到 order:locked 隊(duì)列,表示鎖票成功。

如果鎖票失敗(即數(shù)據(jù)庫修改失敗),設(shè)置 msg 的狀態(tài)為 TICKET_LOCK_FAIL,同時(shí)發(fā)送消息到 order:fail 隊(duì)列,表示鎖票失敗。

3.4.2 鎖票成功(order)

接下來,由 order 服務(wù)消費(fèi) order:locked 隊(duì)列中的消息,也就是鎖票成功后接下來的操作。

  1. @Transactional 
  2. @RabbitListener(queues = "order:locked"
  3. public void handle(OrderDTO msg) { 
  4.     LOG.info("Get new order to create:{}", msg); 
  5.     if (orderRepository.findOneByUuid(msg.getUuid()) != null) { 
  6.         LOG.info("Msg already processed:{}", msg); 
  7.     } else { 
  8.         Order order = newOrder(msg); 
  9.         orderRepository.save(order); 
  10.         msg.setId(order.getId()); 
  11.     } 
  12.     msg.setStatus("NEW"); 
  13.     rabbitTemplate.convertAndSend("order:pay", msg); 

鎖票成功后,先根據(jù)訂單的 UUID 去訂單數(shù)據(jù)庫查詢,是否已經(jīng)有訂單記錄了,如果有,說明這條消息已經(jīng)被處理了,可以防止訂單的重復(fù)處理(這塊主要是解決冪等性問題)。

如果訂單還沒有被處理,則創(chuàng)建一個(gè)新的訂單對象,并保存到數(shù)據(jù)庫中,創(chuàng)建新訂單對象的時(shí)候,需要設(shè)置訂單的 status 為 NEW。

最后設(shè)置 msg 的 status 為 NEW,然后向 order:pay 隊(duì)列發(fā)送一條消息開啟付款流程,付款是由 user 服務(wù)提供的。user 服務(wù)中會(huì)檢查用戶的賬戶余額是否夠用,如果不夠用,就會(huì)發(fā)送消息到 order:ticket_error 隊(duì)列,表示訂票失敗;如果余額夠用,則進(jìn)行正常的付款操作,并在付款成功后發(fā)送消息到 order:ticket_move 隊(duì)列,開啟票的轉(zhuǎn)移。

3.4.3 繳費(fèi)(user)

鎖票成功后,接下來就是付費(fèi)了,付費(fèi)服務(wù)由 user 提供。

  1. @Transactional 
  2. @RabbitListener(queues = "order:pay"
  3. public void handle(OrderDTO msg) { 
  4.     LOG.info("Get new order to pay:{}", msg); 
  5.     // 先檢查payInfo判斷重復(fù)消息。 
  6.     PayInfo pay = payInfoRepository.findOneByOrderId(msg.getId()); 
  7.     if (pay != null) { 
  8.         LOG.warn("Order already paid, duplicated message."); 
  9.         return
  10.     } 
  11.     Customer customer = customerRepository.getById(msg.getCustomerId()); 
  12.     if (customer.getDeposit() < msg.getAmount()) { 
  13.         LOG.info("No enough deposit, need amount:{}", msg.getAmount()); 
  14.         msg.setStatus("NOT_ENOUGH_DEPOSIT"); 
  15.         rabbitTemplate.convertAndSend("order:ticket_error", msg); 
  16.         return
  17.     } 
  18.     pay = new PayInfo(); 
  19.     pay.setOrderId(msg.getId()); 
  20.     pay.setAmount(msg.getAmount()); 
  21.     pay.setStatus("PAID"); 
  22.     payInfoRepository.save(pay); 
  23.     customerRepository.charge(msg.getCustomerId(), msg.getAmount()); 
  24.     msg.setStatus("PAID"); 
  25.     rabbitTemplate.convertAndSend("order:ticket_move", msg); 

這里的執(zhí)行步驟如下:

  1. 首先根據(jù)訂單 id 去查找付款信息,檢查當(dāng)前訂單是否已經(jīng)完成付款,如果已經(jīng)完成服務(wù),則直接 return,這一步也是為了處理冪等性問題。
  2. 根據(jù)顧客的 id,查找到顧客的完整信息,包括顧客的賬戶余額。
  3. 檢查顧客的賬戶余額是否足夠支付票價(jià),如果不夠,則設(shè)置 msg 的 status 為 NOT_ENOUGH_DEPOSIT,同時(shí)向 order:ticket_error 隊(duì)列發(fā)送消息,表示訂票失敗。
  4. 如果顧客賬戶余額足夠支付票價(jià),則創(chuàng)建一個(gè) PayInfo 對象,設(shè)置相關(guān)的支付信息,并存入 pay_info 表中。
  5. 調(diào)用 charge 方法完成顧客賬戶余額的扣款。
  6. 發(fā)送消息到 order:ticket_move 隊(duì)列中,開啟交票操作。

3.4.4 交票(ticket)

  1. @Transactional 
  2. @RabbitListener(queues = "order:ticket_move"
  3. public void handleTicketMove(OrderDTO msg) { 
  4.     LOG.info("Get new order for ticket move:{}", msg); 
  5.     int moveCount = ticketRepository.moveTicket(msg.getCustomerId(), msg.getTicketNum()); 
  6.     if (moveCount == 0) { 
  7.         LOG.info("Ticket already transferred."); 
  8.     } 
  9.     msg.setStatus("TICKET_MOVED"); 
  10.     rabbitTemplate.convertAndSend("order:finish", msg); 

調(diào)用 moveTicket 方法完成交票操作,也就是設(shè)置 ticket 表中票的 owner 為 customerId。

交票成功后,發(fā)送消息到 order:finish 隊(duì)列,表示交票完成。

3.4.5 訂單完成(order)

  1. @Transactional 
  2. @RabbitListener(queues = "order:finish"
  3. public void handleFinish(OrderDTO msg) { 
  4.     LOG.info("Get finished order:{}", msg); 
  5.     Order order = orderRepository.getById(msg.getId()); 
  6.     order.setStatus("FINISH"); 
  7.     orderRepository.save(order); 

這里的處理就比較簡單,訂單完成后,就設(shè)置訂單的狀態(tài)為 FINISH 即可。

上面介紹的是一條主線,順利的話,消息順著這條線走一遍,一個(gè)訂單就處理完成了。

不順利的話,就有各種幺蛾子,我們分別來看。

3.4.6 鎖票失敗(order)

鎖票是在 ticket 服務(wù)中完成的,如果鎖票失敗,就會(huì)直接向 order:fail 隊(duì)列發(fā)送消息,該隊(duì)列的消息由 order 服務(wù)負(fù)責(zé)消費(fèi)。

3.4.7 扣款失敗(ticket)

扣款操作是在 user 中完成的,扣款失敗就會(huì)向 order:ticket_error 隊(duì)列中發(fā)送消息,該隊(duì)列的消息由 ticket 服務(wù)負(fù)責(zé)消費(fèi)。

  1. @Transactional 
  2. @RabbitListener(queues = "order:ticket_error"
  3. public void handleError(OrderDTO msg) { 
  4.     LOG.info("Get order error for ticket unlock:{}", msg); 
  5.     int count = ticketRepository.unMoveTicket(msg.getCustomerId(), msg.getTicketNum()); 
  6.     if (count == 0) { 
  7.         LOG.info("Ticket already unlocked:", msg); 
  8.     } 
  9.     count = ticketRepository.unLockTicket(msg.getCustomerId(), msg.getTicketNum()); 
  10.     if (count == 0) { 
  11.         LOG.info("Ticket already unmoved, or not moved:", msg); 
  12.     } 
  13.     rabbitTemplate.convertAndSend("order:fail", msg); 

當(dāng)扣款失敗的時(shí)候,做三件事:

  1. 撤銷票的轉(zhuǎn)移,也就是把票的 owner 字段重新置為 null。
  2. 撤銷鎖票,也就是把票的 lock_user 字段重新置為 null。
  3. 向 order:fail 隊(duì)列發(fā)送訂單失敗的消息。

3.4.8 下單失敗(order)

下單失敗的處理在 order 服務(wù)中,有三種情況會(huì)向 order:fail 隊(duì)列發(fā)送消息:

  1. 鎖票失敗
  2. 扣款失敗(客戶賬戶余額不足)
  3. 訂單超時(shí)
  1. @Transactional 
  2. @RabbitListener(queues = "order:fail"
  3. public void handleFailed(OrderDTO msg) { 
  4.     LOG.info("Get failed order:{}", msg); 
  5.     Order order
  6.     if (msg.getId() == null) { 
  7.         order = newOrder(msg); 
  8.         order.setReason("TICKET_LOCK_FAIL"); 
  9.     } else { 
  10.         order = orderRepository.getById(msg.getId()); 
  11.         if (msg.getStatus().equals("NOT_ENOUGH_DEPOSIT")) { 
  12.             order.setReason("NOT_ENOUGH_DEPOSIT"); 
  13.         } 
  14.     } 
  15.     order.setStatus("FAIL"); 
  16.     orderRepository.save(order); 

該方法的具體處理邏輯如下:

  1. 首先查看是否有訂單 id,如果連訂單 id 都沒有,就說明是鎖票失敗,給訂單設(shè)置 reason 屬性的值為TICKET_LOCK_FAIL。
  2. 如果有訂單 id,則根據(jù) id 查詢訂單信息,并判斷訂單狀態(tài)是否為 NOT_ENOUGH_DEPOSIT,這個(gè)表示扣款失敗,如果訂單狀態(tài)是 NOT_ENOUGH_DEPOSIT,則設(shè)置失敗的 reason 也為此。
  3. 最后設(shè)置訂單狀態(tài)為 FAIL,然后更新數(shù)據(jù)庫中的訂單信息即可。

3.4.9 訂單超時(shí)(order)

order 服務(wù)中還有一個(gè)定時(shí)任務(wù),定時(shí)去數(shù)據(jù)庫中撈取那些處理失敗的訂單,如下:

  1. @Scheduled(fixedDelay = 10000L) 
  2. public void checkInvalidOrder() { 
  3.     ZonedDateTime checkTime = ZonedDateTime.now().minusMinutes(1L); 
  4.     List<Order> orders = orderRepository.findAllByStatusAndCreatedDateBefore("NEW", checkTime); 
  5.     orders.stream().forEach(order -> { 
  6.         LOG.error("Order timeout:{}"order); 
  7.         OrderDTO dto = new OrderDTO(); 
  8.         dto.setId(order.getId()); 
  9.         dto.setTicketNum(order.getTicketNum()); 
  10.         dto.setUuid(order.getUuid()); 
  11.         dto.setAmount(order.getAmount()); 
  12.         dto.setTitle(order.getTitle()); 
  13.         dto.setCustomerId(order.getCustomerId()); 
  14.         dto.setStatus("TIMEOUT"); 
  15.         rabbitTemplate.convertAndSend("order:ticket_error", dto); 
  16.     }); 

可以看到,這里是去數(shù)據(jù)庫中撈取那些狀態(tài)為 NEW 并且是 1 分鐘之前的訂單,根據(jù)前面的分析,當(dāng)鎖票成功后,就會(huì)將訂單的狀態(tài)設(shè)置為 NEW 并且存入數(shù)據(jù)庫中。換言之,當(dāng)鎖票成功一分鐘之后,這張票還沒有賣掉,就設(shè)置訂單超時(shí),同時(shí)向 order:ticket_error 隊(duì)列發(fā)送一條消息,這條消息在 ticket 服務(wù)中被消費(fèi),最終完成撤銷交票、撤銷鎖票等操作。

這就是大致的代碼處理流程。

再來回顧一下前面那張圖:

結(jié)合著代碼來看這張圖是不是就很容易懂了。

3.5 測試

接下來我們來進(jìn)行一個(gè)簡單的測試。

先來一個(gè)訂票失敗的測試,如下:

由于用戶只有 1000 塊錢,這張票要 10000,所以購票必然失敗。請求執(zhí)行成功后,我們查看 order 表,多了如下一條記錄:

可以看到,訂單失敗的理由就是賬戶余額不足。此時(shí)查看 ticket 和 user 表,發(fā)現(xiàn)都完好如初(如果需要,則已經(jīng)反向補(bǔ)償了)。

接下來我們手動(dòng)給 ticket 表中 lock_user 字段設(shè)置一個(gè)值,如下:

這個(gè)表示這張票已經(jīng)被人鎖定了。

然后我們發(fā)起一次購票請求(這次可以把金額設(shè)置到合理范圍,其實(shí)不設(shè)置也行,反正這次失敗還沒走到付款這一步):

請求發(fā)送成功后,接下來我們?nèi)ゲ榭?order 表,多了如下一條記錄:

可以看到,這次下單失敗的理由是鎖票失敗。此時(shí)查看 ticket 和 user 表,發(fā)現(xiàn)都完好如初(如果需要,則已經(jīng)反向補(bǔ)償了)。

最后再來一次成功測試,先把 ticket 表中的 lock_user 字段置空,然后發(fā)送如下請求:

這次購票成功,查看 ticket 表,發(fā)票已經(jīng)票有所屬:

查看訂單表:

可以多了一條成功的購票記錄。

查看用戶表:

用戶賬戶已扣款。

查看支付記錄表:

可以看到已經(jīng)有了支付記錄。

4. 總結(jié)

整體上來說,上面這個(gè)案例,技術(shù)上并沒有什么難的,復(fù)雜之處在于設(shè)計(jì)。一開始要設(shè)計(jì)好消息的處理流程以及消息處理失敗后如何進(jìn)行補(bǔ)償,這個(gè)是比較考驗(yàn)大家技術(shù)的。

另外上面案例中,消息的發(fā)送和消費(fèi)都用到了 RabbitMQ 中的事務(wù)機(jī)制(確保消息消費(fèi)成功)以及 Spring 中的事務(wù)機(jī)制(確保消息發(fā)送和數(shù)據(jù)保存同時(shí)成功),這些我就不再贅述了。

總之,通過消息中間件處理分布式事務(wù),這種方式通過犧牲數(shù)據(jù)的強(qiáng)一致性換取性能的大幅提升,但是實(shí)現(xiàn)這種方式的成本和復(fù)雜度是比較高的,使用時(shí)還要看實(shí)際業(yè)務(wù)情況。

本文轉(zhuǎn)載自微信公眾號(hào)「江南一點(diǎn)雨」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系江南一點(diǎn)雨公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 江南一點(diǎn)雨
相關(guān)推薦

2022-06-13 10:42:21

分布式事務(wù)數(shù)據(jù)庫

2014-01-22 13:37:53

2014-02-11 09:07:31

2009-02-05 11:39:41

Oracle甲骨文Tuxedo

2015-03-18 09:33:41

大數(shù)據(jù)分布式系統(tǒng)事務(wù)處理

2023-12-29 08:14:41

BASE事務(wù)ServiceB

2019-07-30 07:26:26

技術(shù)分布式指標(biāo)

2015-03-16 14:38:16

大數(shù)據(jù)存儲(chǔ)分布式系統(tǒng)事務(wù)處理

2019-11-18 10:19:02

分布式系統(tǒng)事務(wù)模型

2023-08-16 11:43:57

數(shù)據(jù)引擎

2023-11-01 10:11:00

Java分布式

2023-12-07 08:37:49

TCC模式

2009-07-15 17:41:55

iBATIS事務(wù)處理

2011-04-27 15:55:16

2009-07-09 18:15:42

JDBC事務(wù)處理

2009-09-14 19:55:03

LINQ事務(wù)處理

2010-01-04 13:06:50

ADO.NET事務(wù)

2009-11-13 17:01:07

ADO.NET事務(wù)處理

2010-04-13 15:44:00

Oracle與SqlS

2025-04-29 04:00:00

分布式事務(wù)事務(wù)消息
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

亚洲国产精品一区二区www| 久久综合电影| 色猫猫国产区一区二在线视频| 裸模一区二区三区免费| 亚洲天堂手机在线| 在线欧美不卡| 在线观看欧美日韩| 国产艳妇疯狂做爰视频| 精品日韩视频| 亚洲一二三区在线观看| 手机成人在线| 天天操天天操天天干| 裸体在线国模精品偷拍| 97视频在线观看免费高清完整版在线观看 | 色8久久人人97超碰香蕉987| 中文精品一区二区三区| 天堂中文网在线| 中文字幕成在线观看| 国产精品久久久久久久久久妞妞| 最近2019年手机中文字幕| 日本美女视频网站| 992tv国产精品成人影院| 一区二区三区蜜桃网| 日韩欧美视频一区二区| 韩国av在线免费观看| 蜜桃久久av一区| 欧美一级淫片videoshd| 激情五月少妇a| 日本电影一区二区| 亚洲男人天堂视频| 亚洲熟女一区二区| 一区二区三区在线免费看| 欧美性大战久久久| av免费中文字幕| av手机免费在线观看| 亚洲人成亚洲人成在线观看图片| 视频一区二区三区在线观看| 性感美女福利视频| 成人av免费在线播放| 91久久极品少妇xxxxⅹ软件| 国产麻豆免费视频| 日韩精品欧美精品| 国产国语videosex另类| 亚洲午夜18毛片在线看| 一区二区高清| 91精品国产高清自在线 | 亚洲视频1区2区| 四虎永久在线精品免费一区二区| 青青久在线视频| 91丨porny丨蝌蚪视频| 精品一区久久久| 性感美女一级片| 91丝袜呻吟高潮美腿白嫩在线观看| 国产综合第一页| 四虎精品在永久在线观看| 97久久精品人人澡人人爽| 国产精品一区二区三区四区五区 | 日韩三级视频在线| 亚洲美女毛片| 91精品国产电影| 四虎成人在线观看| 久久永久免费| 国产精品亚洲综合天堂夜夜| 这里只有精品9| 久草在线在线精品观看| 亚洲自拍欧美另类| 人妻va精品va欧美va| 91丨porny丨国产| 色就是色欧美| 黄色网页在线观看| 一区二区国产盗摄色噜噜| 男人添女荫道口女人有什么感觉| 密臀av在线| 精品久久久久久国产| 成人av一级片| 亚洲天堂一区二区| 91精品在线一区二区| avtt中文字幕| 少妇精品久久久一区二区| 一区二区av在线| 视频这里只有精品| 一本久道久久综合狠狠爱| 奇米4444一区二区三区| 97超视频在线观看| 成人综合激情网| 日韩精品不卡| 性欧美video高清bbw| 午夜一区二区三区视频| 久久精品网站视频| 中文字幕一区二区三区四区久久 | 国产日韩一区| 国产日韩在线视频| 日韩一区免费视频| 中文字幕欧美国产| 久久99久久99精品| 日本欧美韩国| 精品日韩欧美在线| avhd101老司机| 激情婷婷亚洲| 国产欧美精品久久久| 日本高清视频在线| 中文字幕一区二| 黄色动漫在线免费看| 欧美91在线|欧美| 亚洲精品国产综合久久| 特一级黄色录像| 久久综合导航| 国产精品三区www17con| 日本在线观看免费| 一本到高清视频免费精品| 91视频免费入口| 欧美综合视频| 奇米4444一区二区三区| 亚洲国产精品久久久久久久 | 免费一级肉体全黄毛片| 日本欧美大码aⅴ在线播放| 国产精品亚洲不卡a| 欧美黄色激情| 色视频成人在线观看免| 亚洲av成人片无码| 综合在线视频| 国产九九精品视频| 国产精品视频二区三区| 精品人伦一区二区三区蜜桃网站 | 91精品无人成人www| 欧美日韩一区二区三区四区不卡| 久久亚洲一区二区三区四区五区高| 中文字幕xxxx| 久久综合狠狠综合久久激情| 欧美午夜性视频| 日韩精品中文字幕一区二区 | 免费毛片一区二区三区久久久| 日本伦理一区二区| 欧美一区二区三区视频免费| 影音先锋男人资源在线观看| 青青青伊人色综合久久| 欧美自拍资源在线| 国产精品av一区二区三区| 日韩av一区二区在线| 久草精品视频在线观看| 成人国产精品免费观看| 日本免费a视频| 在线播放一区二区精品视频| 欧美成人在线免费视频| av 一区二区三区| 亚洲精品第一国产综合野| 午夜激情视频网| 欧美精品黄色| 国产精品18毛片一区二区| 久久免费电影| 亚洲高清av在线| 国产成人在线观看网站| 99国产精品国产精品毛片| 久久黄色片视频| 婷婷综合一区| 国产成人在线一区| av在线收看| 制服丝袜一区二区三区| a级黄色片免费看| 懂色中文一区二区在线播放| 精品无码国模私拍视频| 欧美成a人免费观看久久| 欧美自拍视频在线观看| 成人免费一区二区三区视频网站| 欧美性视频一区二区三区| 99精品中文字幕| 国产精品一区二区三区乱码| 免费特级黄色片| 要久久爱电视剧全集完整观看| 国产精品第8页| 黄色在线免费| 亚洲丁香婷深爱综合| aaaaaa毛片| 亚洲男人都懂的| 亚洲国产一区在线观看| 日韩精品一区二区三区丰满| 欧美黄色成人| 九九精品视频在线| 天堂av在线资源| 欧美午夜精品一区二区三区| 国产av无码专区亚洲av毛网站| 成人免费毛片aaaaa**| 成熟老妇女视频| 亚洲国产成人精品女人| 久久99精品久久久久久久青青日本| 神马电影网我不卡| 久久精品青青大伊人av| 香蕉视频免费在线看| 欧美日韩国产综合一区二区三区| 久久免费在线观看视频| 国产欧美视频一区二区三区| 国产亚洲精品成人a| 美女尤物久久精品| 四虎精品欧美一区二区免费| 综合色就爱涩涩涩综合婷婷| 91免费福利视频| 成人小电影网站| 欧美大奶子在线| 第一页在线观看| 亚洲国产精品字幕| 97在线视频人妻无码| 欧美午夜精品久久久久久浪潮| 永久免费看片直接| 91麻豆国产福利精品| 亚洲成人av免费观看| 日韩制服丝袜先锋影音| 久青草视频在线播放| 国产精品99久久久久久动医院| 国产一级精品aaaaa看| 久久伦理中文字幕| 国产精品久久色| 成入视频在线观看| 久久综合免费视频影院| 国产女人在线观看| 亚洲精品福利视频| 精品人妻少妇AV无码专区| 在线免费一区三区| 91精品国产乱码在线观看| 亚洲欧美韩国综合色| 手机看片国产日韩| 久久精品一二三| 色天使在线视频| www.性欧美| 丰满熟女人妻一区二区三区| 国产在线视频不卡二| 亚洲成人福利在线观看| 国产精品亚洲欧美| 欧美视频在线观看网站 | 性高湖久久久久久久久aaaaa| 欧美超碰在线| 亚洲一区二区不卡视频| 成人同人动漫免费观看| 欧美日本韩国一区二区三区| 精品视频自拍| 国产精品日韩一区二区三区 | 精品久久久久久无| av网站在线免费看| 91精品国产黑色紧身裤美女| 国产伦精品一区二区三区四区 | 国产特级黄色片| 欧美精品乱码久久久久久按摩 | 日韩av高清在线观看| 可以在线看的黄色网址| 亚洲一区二区免费看| 黄色免费视频大全| 亚洲欧美春色| 欧美日韩第二页| 天堂影院一区二区| 爱情岛论坛亚洲首页入口章节| 日韩电影在线免费| 亚欧美在线观看| 久久电影网电视剧免费观看| www.污污视频| 国产高清不卡二三区| 动漫av在线免费观看| 99久久精品99国产精品| 男女黄床上色视频| 欧美韩国日本不卡| 黄色一级大片在线免费观看| 亚洲黄网站在线观看| 国产一卡二卡在线| 色婷婷精品久久二区二区蜜臂av| 手机av免费观看| 欧美另类一区二区三区| 国产黄色av网站| 亚洲激情久久久| 九色在线观看| 精品国产一区二区三区在线观看| 毛片在线看网站| 久久久久久久久久久免费精品| 日本不卡网站| 成人黄色免费看| 国产成人av毛片| 蜜桃成人在线| 亚州av乱码久久精品蜜桃| 久久精品人人做人人爽人人| 国产91在线免费观看| 成人黄色一级视频| 一区二区三区伦理片| 国产精品久久久久久久久快鸭| 99精品久久久久| 欧美日韩一区二区免费视频| 亚洲熟妇无码久久精品| 精品美女一区二区三区| 国产在线资源| 精品中文字幕在线观看| 性欧美hd调教| 97超碰在线播放| 国产麻豆一区二区三区精品视频| 亚洲成人动漫在线| 亚洲免费影视| 肉丝美足丝袜一区二区三区四| 久久色在线视频| 性色av无码久久一区二区三区| 欧美性猛交xxxx富婆| 国产人妻精品一区二区三| 亚洲精品理论电影| 精品视频在线一区二区| 奇米4444一区二区三区| 在这里有精品| 波多野结衣激情| 丝袜亚洲精品中文字幕一区| 粗大的内捧猛烈进出视频| 欧美国产乱子伦 | 蜜桃视频网站在线观看| 欧美一区在线直播| 99国产精品久久一区二区三区| 亚洲 国产 欧美一区| 在线午夜精品| 中文字幕一区二区三区人妻在线视频 | 国产高清视频免费最新在线| 欧美精品第一页在线播放| 欧美亚洲二区| 日韩高清国产一区在线观看| 亚洲三级视频| 一二三区视频在线观看| 亚洲欧美在线另类| 中文字幕黄色av| 亚洲色图国产精品| 亚洲欧洲自拍| 精品不卡一区二区三区| 狠久久av成人天堂| 三级网站免费看| 亚洲天堂福利av| 中文字幕日韩第一页| 亚洲欧洲日产国码av系列天堂| www.综合| 国产日韩欧美一区二区| 欧美日韩综合| xxxx国产视频| 一区二区三区中文字幕| av老司机久久| 欧美成人免费在线视频| 国产精品成人**免费视频| 中文字幕免费在线不卡| 久久www免费人成看片高清| 9.1片黄在线观看| 欧美日韩一区二区三区高清 | 九九精品视频在线观看| 欧美成人一级| www.xxx麻豆| 成人精品小蝌蚪| 日本五十熟hd丰满| 日韩成人黄色av| 成人免费看视频网站| 欧美最大成人综合网| 久热综合在线亚洲精品| 中文字幕 自拍| 欧美优质美女网站| 免费黄色在线| 亚洲在线www| 亚洲精品麻豆| 亚洲做受高潮无遮挡| 91成人免费在线| 91精彩在线视频| 91免费高清视频| 1000部精品久久久久久久久| 午夜不卡久久精品无码免费| 欧美性xxxxxx| 自拍视频在线网| www.成人av| 久久成人免费| 国产精品一区二区亚洲| 日韩欧美中文一区| 女人让男人操自己视频在线观看| 欧美色欧美亚洲另类七区| 青青草成人在线观看| tube国产麻豆| 日韩av中文在线| 成人免费毛片嘿嘿连载视频…| 中文字幕一区二区三区有限公司| 国产麻豆91精品| 日韩人妻无码一区二区三区99 | 欧美一区二区三区的| av中文在线资源| 日产精品久久久一区二区| 精品一区二区三区av| 国产精品99无码一区二区| 亚洲日本aⅴ片在线观看香蕉| 亚洲人成777| 精品少妇在线视频| 中文字幕不卡在线观看| 丰满熟女一区二区三区| 国产精品久久久久久搜索| 欧美久久久久| 久久久久久国产免费a片| 日韩亚洲欧美成人一区| 澳门成人av网| 久久最新免费视频| 久久亚洲一区二区三区明星换脸| 国产精品视频第一页| 69视频在线播放| 夜间精品视频| 亚洲区免费视频| 欧美成人性福生活免费看| 欧美日韩精品免费观看视完整| 国产精品视频二| 欧美国产视频在线| 手机在线观看毛片| 成人网在线免费看|