1. 程式人生 > >基於可靠訊息方案的分散式事務(三):Lottor使用

基於可靠訊息方案的分散式事務(三):Lottor使用

前面兩篇文章介紹了筆者關於可靠訊息方案的分散式事務的實現思路以及Java中的事務概念,奈何工作抽不出時間,如今時隔已久,分散式事務系列拖了很久,一直沒能好好把專案和文章整理一下,實在心裡有愧。

Lottor介紹

Lottor用於解決微服務架構下分散式事務的問題,基於可靠性訊息事務模型實現。

Lottor的結構

Lottor由三部分組成:

  • Lottor Server
  • Lottor Client
  • Lottor Admin UI

Lottor伺服器與客戶端之間使用Netty通訊。所有的客戶端(生產端和消費端)都會與伺服器保持長連線。Lottor Admin UI用於展示系統中的事務組詳細資訊,包括預提交的事務組、消費失敗的事務訊息,並支援頁面操作失敗的訊息(如重試)。

Lottor的設計

關於Lottor的實現思路簡略可以參見基於可靠訊息方案的分散式事務:Lottor介紹,後面會詳細介紹。

如何使用

Lottor Client儲存方式支援Redis和MongoDB,Lottor Server目前資料儲存只支援MongoDB。Lottor客戶端和伺服器都會註冊到服務發現元件,支援Consul、zookeeper、Eureka。目前對於Spring Cloud的整合更為方便,Lottor Server伺服器傳送事務訊息到Lottor Client消費方時,使用了Spring Cloud整合的訊息驅動元件Spring Cloud Stream,不過這屬於弱耦合,Lottor暫時不考慮這部分的解耦。

體驗一下Lottor準備的samples,需要準備如下元件:

  1. 安裝好Consul或其他服務發現元件;
  2. 訊息中介軟體rabbitmq或kafka(目前Spring Cloud Stream完全適配這兩種訊息中介軟體);
  3. MongoDB,Lottor Server儲存的方式;
  4. Redis(可選),客戶端的儲存方式,也可為Redis。

如果你想很快嘗試專案中的Samples,請選擇如上的準備事項的第一個選擇,避免耽誤你的時間。

具體啟動步驟:

  1. 首先需要啟動Lottor Server,有兩個埠,9888用於Netty Server通訊,9666為對外暴露的Http埠(用於提供Lottor Admin UI的介面和Lottor Client連線);
  2. 啟動lottor-samples下的lottor-demo-consumer,埠為8007;
  3. 啟動lottor-samples下的lottor-demo-producer,埠為8009。

啟動好如上三個服務之後,將會在兩個客戶端服務的控制檯看到如下的日誌:

c.b.l.c.n.impl.NettyClientServiceImpl   : 連線到Lottor Server【127.0.0.1:9998】
c.b.l.c.n.impl.NettyClientServiceImpl   : Connect to【127.0.0.1:9998】c.b.l.c.n.h.NettyClientMessageHandler   : 成功連線到Lottor Server
c.b.l.c.cache.impl.TxOperateServiceImpl : 啟動OperatePool操作執行緒數量為:8
c.b.l.core.service.impl.InitServiceImpl : 分散式事務Cache初始化成功!
o.s.s.c.ThreadPoolTaskScheduler         : Initializing ExecutorService  'taskScheduler'

客戶端還會按照一定的時間週期向Lottor Server傳送心跳,並收到服務端的心跳回應。如下所示:

c.b.l.c.n.h.NettyClientMessageHandler : 傳送【心跳】事件到Lottor Server【127.0.0.1:9998】
c.b.l.c.n.h.NettyClientMessageHandler : 接收到 Lottor 服務端 【127.0.0.1:9998】 的【心跳】事件

Docker啟動

為了讓讀者更方便的在本地嚐鮮,在專案中提供了docker-compose.yml用以便捷且快速地啟動相關的中介軟體。在docker-compose.yml中包含了如下元件的配置:

  • Lottor UI,Lottor 的前端專案Dashboard,用以展示分散式事務呼叫的相關資訊,包括事務組的狀態、事務訊息的狀態以及異常原因。
  • Consul-0.8.5,服務發現與註冊
  • MongoDB-3.2,持久化儲存
  • Redis-4,持久化儲存
  • Rabbitmq-3.6-management,訊息中介軟體
  • Mysql-5.7,例項的user服務和auth服務儲存

讀者在本地安裝好docker-compose之後,即可一鍵啟動這些元件服務。

docker-compose up -d

呼叫

Lottor-Samples中的場景為:呼叫User服務建立一個使用者,使用者服務的user表中增加了一條使用者記錄。除此之外,還會呼叫Auth服務建立該使用者對應的角色和許可權資訊。

User提供的API端點

User服務只提供了一個建立使用者介面,通過請求引數來模擬建立使用者時會遇到的情況:

  • 成功生產且成功消費

    User服務成功執行本地事務併發送確認訊息,Lottor Server接收到確認的事務組訊息,然後Lottor Server傳送事務訊息到Auth服務,Auth服務成功消費並向Lottor Server響應訊息消費成功的狀態。

    訪問lottor-samples-auth提供的API介面http://localhost:8009/user?result=success即可看到User、Auth和Lottor Server控制檯的日誌資訊。

    User日誌資訊:

    傳送preCommit訊息
    開始建立Lottor事務組,  事務組 id 為【1172893261】
    接收到 Lottor 服務端 【127.0.0.1:9998】 的【接收】事件
    傳送事務組confirm訊息, 本地事務完成狀態為【true】
    事務發起方事務組confirm, 事務組 id 為【1172893261】
    事務組【1172893261】, confirm status為【已經提交】
    事務組【1172893261】成功傳送確認訊息

    Lottor Server日誌資訊:

    Lottor Server接收到客戶端【127.0.0.1:62481】的【建立事務組】事件
    Lottor Server接收到客戶端【127.0.0.1:62481】的【完成提交】事件
    send tx-msg and target service【auth】
    success send msg, and msg id is1958885429

    Auth日誌資訊:

    ===============consume notification message: =======================
    TransactionMsg(groupId=1172893261, subTaskId=1958885429, source=user, target=auth, method=auth-role, args=UserRoleDTO(roleEnum=ADMIN, userId=0df80f61-2802-4dfc-9119-1de97e3b3a00), createTime=1533478144000, message=null, updateTime=null, consumed=0)
    auth-role
    matched case auth-role
    傳送Consume訊息,groupId【1172893261】 and subTaskId【1958885429】,消費結果為【truetx-transaction 消費完成,事務組 id 為【1172893261】,訊息 id 為【1958885429】
  • 生產方本地事務執行失敗

    User服務執行本地事務失敗,併發送事務組回滾的訊息到Lottor Server,取消該事務組中的訊息傳送。

    訪問lottor-samples-user提供的API介面http://localhost:8009/user?result=fail即可看到User服務和Lottor Server控制檯的日誌資訊。

    User服務的日誌資訊:

    傳送preCommit訊息
    開始建立Lottor事務組,  事務組 id 為【1964885182】
    接收到 Lottor 服務端 【127.0.0.1:9998】 的【接收】事件
    SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, 傳送事務組confirm訊息, 本地事務完成狀態為【false】
    事務發起方事務組confirm, 事務組 id 為【1964885182】
    務組【1964885182】, confirm status為【回滾】
    事務組【1964885182】成功傳送確認訊息
    執行本地事務失敗,cause is 【
    
    ### Error updating database.  Cause: java.sql.SQLException: Column count doesn't match value count at row 1
    
    
    ### The error may involve com.blueskykong.lottor.samples.user.service.mapper.UserMapper.saveUserFailure-Inline
    
    
    ### The error occurred while setting parameters
    
    
    ### SQL: INSERT INTO user(id,username,password,self_desc) VALUES(?,?)
    
    
    ### Cause: java.sql.SQLException: Column count doesn't match value count at row 1
    
    ; bad SQL grammar []; nested exception is java.sql.SQLException: Column count doesn't match value count at row 1

    Lottor Server日誌資訊:

    Lottor Server接收到客戶端【127.0.0.1:62481】的【建立事務組】事件
    Lottor Server接收到客戶端【127.0.0.1:62481】的【完成提交】事件
  • 成功生產但消費失敗

    User服務成功執行本地事務併發送確認訊息,Lottor Server接收到確認的事務組訊息,然後Lottor Server傳送事務訊息到Auth服務,Auth服務消費失敗,並向Lottor Server響應訊息消費失敗的狀態。

    訪問lottor-samples-user提供的API介面http://localhost:8009/user?result=fail即可看到User、Auth和Lottor Server控制檯的日誌資訊。這裡只展示Auth服務的日誌資訊,其他兩個服務的日誌資訊可以參見第一種情況。

    Auth日誌資訊:

    ===============consume notification message: =======================
    TransactionMsg(groupId=1177311097, subTaskId=1713403133, source=user, target=auth, method=auth-role, args=UserRoleDTO(roleEnum=ADMIN, userId=null), createTime=1533478468000, message=null, updateTime=null, consumed=0)
    auth-role
    SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
    
    ### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
    
    
    ### The error may involve com.blueskykong.lottor.samples.auth.service.mapper.RoleUserMapper.saveRoleUser-Inline
    
    
    ### The error occurred while setting parameters
    
    
    ### SQL: INSERT INTO user_role(id,user_id,role_id) VALUES(?,?,?)
    
    
    ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
    
    ; SQL []; Column 'user_id' cannot be null; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
    傳送Consume訊息,groupId【1177311097and subTaskId【1713403133】,消費結果為【false】
    tx-transaction 消費完成,事務組 id 為【1177311097】,訊息 id 為【1713403133

專案截圖

專案結構

開啟Lottor DashBoard,地址為http://127.0.0.1:3000/lottor(Lottor Server的地址可以通過docker-compose中的環境變數SERVER_TAG配置,預設為http://127.0.0.1:9666),可以看到如下頁面:

UI首頁

事務組資訊

事務組狀態

致謝

Lottor的具體實現上,參考了2PC的分散式事務解決方案happylifeplat-transaction的通訊框架。最近看了一下,發現已經更名為Raincat,讀者欲瞭解更多,可以參見https://github.com/yu199195/Raincat,在此致謝。

Lottor專案地址:https://github.com/keets2012/Lottor

訂閱最新文章,歡迎關注我的公眾號

微信公眾號