回覆列表
  • 1 # CoreCode

    我們從一個簡單的例項入手. 基本所有網際網路應用都會有使用者註冊的功能. 在這個例子中, 我們對於使用者註冊有兩步操作:

    1. 註冊成功, 儲存使用者資訊.

    2. 需要給使用者發放一張代金券, 目的是鼓勵使用者進行消費.如果是一個單一架構應用, 實現這個功能非常簡單: 在一個本地事務裡, 往使用者表插一條記錄, 並且在代金券表裡插一條記錄, 提交事務就完成了. 但是如果我們的應用是用微服務實現的, 可能使用者和代金券是兩個獨立的服務, 他們有各自的應用和資料庫, 那麼就沒有辦法簡單的使用本地事務來保證操作的原子性了. 現在來看看如何使用事件機制和訊息佇列來實現這個需求.(我在這裡使用的訊息佇列是kafka, 原理同樣適用於ActiveMQ/RabbitMQ等其他佇列)我們會為使用者註冊這個操作建立一個事件, 該事件就叫做使用者建立事件(USER_CREATED).

    使用者服務成功儲存使用者記錄後, 會發送使用者建立事件到訊息佇列, 代金券服務會監聽使用者建立事件, 一旦接收到該事件, 代金券服務就會在自己的資料庫中為該使用者建立一張代金券. 好了, 這些步驟看起來都相當的簡單直觀, 但是怎麼保證事務的原子性呢? 考慮下面這兩個場景:

    1. 使用者服務在儲存使用者記錄, 還沒來得及向訊息佇列傳送訊息之前就宕機了. 怎麼保證使用者建立事件一定傳送到訊息隊列了?

    2. 代金券服務接收到使用者建立事件, 還沒來得及處理事件就宕機了. 重新啟動之後如何消費之前的使用者建立事件?這兩個問題的本質是: 如何讓操作資料庫和操作訊息佇列這兩個操作成為一個原子操作. 不考慮2PC, 這裡我們可以透過事件表來解決這個問題. 下面是類圖. EventPublish是記錄待發布事件的表. 其中:id: 每個事件在建立的時候都會生成一個全域性唯一ID, 例如UUID.status: 事件狀態, 列舉型別. 現在只有兩個狀態: 待發布(NEW), 已釋出(PUBLISHED).payload: 事件內容. 這裡我們會將事件內容轉成json存到這個欄位裡.eventType: 事件型別, 列舉型別. 每個事件都會有一個型別, 比如我們之前提到的建立使用者USER_CREATED就是一個事件型別.EventProcess是用來記錄待處理的事件. 欄位與EventPublish基本相同.我們首先看看事件的釋出過程. 下面是使用者服務釋出使用者建立事件的順序圖.

    1. 使用者服務在接收到使用者請求後開啟事務, 在使用者表建立一條使用者記錄, 並且在EventPublish表建立一條status為NEW的記錄, payload記錄的是事件內容, 提交事務

    2. 使用者服務中的定時器首先開啟事務, 然後查詢EventPublish是否有status為NEW的記錄, 查詢到記錄之後, 拿到payload資訊, 將訊息釋出到kafka中對應的topic.傳送成功之後, 修改資料庫中EventPublish的status為PUBLISHED, 提交事務.

  • 中秋節和大豐收的關聯?
  • 自動擋哪種變速箱好?