MQ 分佈式事務 -- 微服務應用

一、背景

友情連接:https://www.cnblogs.com/Agui520/p/11187972.htmlhtml

    https://blog.csdn.net/fd2025/article/details/79863390數據庫

以支付、電商下單爲例子。一個電商系統包含了好幾大類模塊,就好比有用戶模塊、商品模塊、庫存模塊、訂單模塊、支付模塊、物流模塊,活動模塊等,如下就先列舉幾個最基礎常見的模塊(用戶模塊、商品模塊、庫存模塊、訂單模塊、支付模塊)。服務器

用戶流程以下:分佈式

 

二、問題

若是系統規模較小,數據表都在一個數據庫實例上,項目服務端也都在同一個項目,那上面的問題基本不是問題,直接用本地事務(一致性,原子性、隔離性、持久性)解決,好比支付轉帳(A->B)模塊確定會出現A帳戶減小,B帳戶增長,程序操做加個事務管理就解決。可是若是系統規模較大,好比支付寶帳戶表和餘額寶帳戶表顯然不會在同一個數據庫實例上,他們每每分佈在不一樣的物理節點上,又好比商品模塊,訂單模塊不會在同一個數據庫中或是在同一個項目中,這時本地事務就已經失去用武之地了。性能

三、場景

1.場景1

支付寶轉1萬元到餘額寶,若是支付寶扣除1萬後,若是系統掛掉了,餘額寶並無增長1萬,數據出現不一致狀況。ui

2.場景2

電商系統中,當有用戶下單後,除了在訂單表插入一條記錄外,對應商品表的這個商品數量必須 減1,怎麼保證??在 搜索廣告系統中,當用戶點擊某廣告後,除了在點擊事件表中 增長一條
記錄外 ,還得去商家帳戶表中找到這個商家扣除廣告費吧,怎麼保證??spa

不拆分服務最多見的解決方案:.net

本地事務:htm

Begin transaction
update A set amount = amount - 10000 where userId = 1
update B set amount = amount + 10000 where userId = 1
End transaction
commit;中間件

四、MQ實現分佈式事務

當 支付寶帳戶扣除1萬後,咱們只要生成一個憑證(消息)便可,這個憑證(消息)上寫着「讓餘額寶帳戶增長一萬」,只要這個憑證(消息)能可靠保證,咱們最終是能夠拿着這個憑證(消息)讓餘額寶帳戶 增長1萬的,即咱們能依靠這個憑證(消息)完成最終一致性。

4.1 如何可靠保存憑證

業務與消息耦合的方式

支付寶在完成扣款的同時,同時記錄消息數據,這個消息數據與業務數據保存在同一數據庫實例裏(消息記錄表名爲message).

Begin transtration

update A set amount = amount -10000 where userId = 1;

insert into message(userId,amount,status) values (1,10000,1);

End transaction

commit;
上述事務能保證只要支付寶帳戶裏被扣了錢,消息必定能保存下來。

當上述事務提交成功後,咱們經過實時消息服務將此消息通知餘額寶,餘額寶處理成功後發送回覆成功消息後,支付寶收到回覆後刪除該條消息數據。

業務與消息解耦

爲了解耦,能夠採起如下方式:

一、支付寶在扣款事務提交以前,向實時消息服務請求發送消息,實時消息服務只記錄消息數據,而不真正發送(只是知道有這一條消息),只有消息發送成功後纔會提交事務。

 

//支付寶 - 10000 (業務需求)

//先把(支付寶-10000)封裝成一個消息(new Message()))

//而後把這個消息提交到MQ服務器上send(producer.send(new Message(),callback(裏面處理本地事務)))

//在callback處理本地事務:在callback方法裏:

update A set amount = amount - 10000 where userId = 1;

..............

//當本地事務操做完成了之後

1.要麼成功:(給MQ一個標識:COMMIT)

2.要麼失敗:(給MQ一個標識:ROLLBACK)
2. 當支付寶扣款事務被提交成功後,向實時 消息服務確認發送,只有在獲得確認發送指令後,實時消息服務才真正發送該消息。

3. 當支付寶扣款事務提交失敗回滾後,向實時 消息服務取消發送。在獲得取消發送指令後, 該消息將不會被髮送。

4. 對於那些未確認的消息或者取消的消息,須要有一個消息狀態確認系統定時去支付寶系統查詢這個消息的 狀態進行更新。爲何須要這一個步驟:假設在支付寶扣款事務被成功提交後,系統掛了,此時消息狀態並未被更新爲「確認發送」,從而致使消息不能被髮送。

優勢:消息數據獨立存儲 ,下降業務系統與消息系統之間的耦合。

缺點:一次消息發送須要兩次請求;業務處理服務須要實現消息狀態回查接口

 

 

 綜合上述的描述,RabbitMQ 作了這麼三件事:

1,先發送須要發送的消息到消息中間件broker,並獲取到該message的 transactionId。在第一次發送的時候,該消息的狀態爲LocalTransactionState.UNKNOW
2,處理本地事物。
3,根據本地事物的執行結果,結合 transactionId,找到該消息的位置,在mq中標誌該消息的最終處理結果。
 

五、Rabbit MQ 介紹

相關連接:https://www.cnblogs.com/duanxz/p/3542320.html

 

RabbitMQ的結構圖以下:

 

一、幾個概念說明:

Broker:簡單來講就是消息隊列服務器實體。
Exchange:消息交換機,它指定消息按什麼規則,路由到哪一個隊列。

    Exchange類型

    A. direct exchange:將與routing key 比配的消息,直接推入相對應的隊列,建立隊列時,默認就建立同名的routing key。

    B. fanout exchange:是一種廣播模式,忽略routingkey的規則。

    C. topic exchange:應用主題,根據key進行模式匹配路由,例如:若爲abc*則推入到全部abc*相對應的queue;若爲abc.#則推入到abc.xx.one ,abc.yy.two對應的queue。


Queue:消息隊列載體,每一個消息都會被投入到一個或多個隊列。
Binding:綁定,它的做用就是把exchange和queue按照路由規則綁定起來。
Routing Key:路由關鍵字,exchange根據這個關鍵字進行消息投遞。
vhost:虛擬主機,一個broker裏能夠開設多個vhost,用做不一樣用戶的權限分離。
producer:消息生產者,就是投遞消息的程序。
consumer:消息消費者,就是接受消息的程序。
channel:消息通道,在客戶端的每一個鏈接裏,可創建多個channel,每一個channel表明一個會話任務。是基於Connection之上創建通訊通道,由於每次Connection創建TCP協議通訊開銷及性能消耗較大,因此一次創建Connection後,使用多個Channel通道通訊減小開銷和提升性能。

 

二、消息隊列的使用過程大概以下:

(1)客戶端鏈接到消息隊列服務器,打開一個channel。
(2)客戶端聲明一個exchange,並設置相關屬性。
(3)客戶端聲明一個queue,並設置相關屬性。
(4)客戶端使用routing key,在exchange和queue之間創建好綁定關係。
(5)客戶端投遞消息到exchange。

exchange接收到消息後,就根據消息的key和已經設置的binding,進行消息路由,將消息投遞到一個或多個隊列裏。

exchange也有幾個類型,徹底根據key進行投遞的叫作Direct交換機,例如,綁定時設置了routing key爲」abc」,那麼客戶端提交的消息,只有設置了key爲」abc」的纔會投遞到隊列。對key進行模式匹配後進行投遞的叫作Topic交換機,符 號」#」匹配一個或多個詞,符號」*」匹配正好一個詞。例如」abc.#」匹配」abc.def.ghi」,」abc.*」只匹配」abc.def」。還 有一種不須要key的,叫作Fanout交換機,它採起廣播模式,一個消息進來時,投遞到與該交換機綁定的全部隊列。

 

 

 

六、代碼實現

相關文章
相關標籤/搜索