終於跑通分佈式事務框架tcc-transaction的示例項目

背景

前段時間在看項目代碼的時候,發現有些接口的流程比較長,在各個服務裏面都有經過數據庫事務保證數據的一致性,可是在上游的controller層並無對一致性作保證。java

網上查了下,還沒找到基於Go開源的比較成熟的分佈式事務框架。mysql

因而,準備看看以前隔壁部門大佬寫的tcc-transaction,這是一個基於tcc思想實現的分佈式事務框架。git

tcc分別代碼Try,Confirm和Cancel。github

Try: 嘗試執行業務web

完成全部業務檢查(一致性)

預留必須業務資源(準隔離性)
複製代碼

Confirm: 確認執行業務redis

真正執行業務

不做任何業務檢查

只使用Try階段預留的業務資源

Confirm操做知足冪等性
複製代碼

Cancel: 取消執行業務spring

釋放Try階段預留的業務資源

Cancel操做知足冪等性
複製代碼

要了解其實現原理,第一步就是跑通項目自帶的示例,即tcc-transaction-tutorial-sample部分的代碼。sql

今天主要介紹在跑通tcc-transaction-tutorial-sample過程當中遇到的各類坑。數據庫

依賴環境

  • Java
  • Maven
  • Git
  • MySQL
  • Redis
  • Zookeeper
  • Intellij IDEA

源碼地址:github.com/changmingxi…apache

我本身Fork了一份(配置改動已提交):github.com/DMinerJacki…

踩坑歷程

踩坑準備

第一步:克隆代碼

使用"git clone github.com/DMinerJacki…"命令下載代碼

第二步:導入代碼並執行數據庫腳本

代碼導入Intellij IDEA。

執行tcc-transaction-http-sample/src/main/dbscripts 下的數據庫腳本。

第三步:修改配置文件

主要修改的是數據庫配置參數。拿tcc-transaction-dubbo-sample舉例,須要修改的文件有

tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-dubbo-sample/tcc-transaction-dubbo-capital/src/main/resources/tccjdbc.properties

tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-dubbo-sample/tcc-transaction-dubbo-redpacket/src/main/resources/tccjdbc.properties

tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-dubbo-sample/tcc-transaction-dubbo-order/src/main/resources/tccjdbc.properties

三個文件修改後對應配置以下

# 根據具體的MySQL版本使用驅動名稱
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
# 換成你鏈接數據庫的地址
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
# 換成你須要配置數據庫的用戶名
jdbc.username=root
# 換成你須要配置數據庫的密碼
jdbc.password=rootroot

c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
複製代碼

同時修改tcc-transaction-sample-capital、tcc-transaction-sample-redpacket和tcc-transaction-sample-order三個項目中jdbc.proerties文件的數據庫鏈接,修改後配置以下

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_CAP?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
jdbc.username=root
jdbc.password=rootroot

c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
複製代碼

第四步:啓動項目

結合項目的README.md文件以及網上的文章瞭解到若是要跑通示例項目,須要分別啓動三個項目。

tcc-transaction提供了兩個版本:

  • 基於dubbo通信的示例版本

  • 基於http通信的示例版本

這兩個版本對於的三個項目分別是

  • tcc-transaction-dubbo-capital(帳戶資產服務)、 tcc-transaction-dubbo-redpacket(紅包服務)、 tcc-transaction-dubbo-order(交易訂單服務)

  • tcc-transaction-http-capital(帳戶資產服務)、 tcc-transaction-http-redpacket(紅包服務)、 tcc-transaction-http-order(交易訂單服務)

其實這兩個版本我都跑過,最終成功跑通的只有基於dubbo通信的示例版本(http版本在最後confirm的時候最是失敗,致使最終訂單狀態爲unkown)。

以基於dubbo通信的示例爲例

tcc-transaction-dubbo-capital的啓動配置以下

tcc-transaction-dubbo-redpacket的啓動配置以下

tcc-transaction-dubbo-order的啓動配置以下

坑1:鏈接不上zk

啓動tcc-transaction-dubbo-capital項目,報錯信息以下

[sample-dubbo-capital]2019-08-31 17:48:05,312 INFO  [org.apache.zookeeper.ZooKeeper] Initiating client connection, connectString=127.0.0.1:2181 sessionTimeout=30000 watcher=org.I0Itec.zkclient.ZkClient@32c7bb63
[sample-dubbo-capital]2019-08-31 17:48:05,334 INFO  [org.apache.zookeeper.ClientCnxn] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) [sample-dubbo-capital]2019-08-31 17:48:05,344 WARN [org.apache.zookeeper.ClientCnxn] Session 0x0 for server null, unexpected error, closing socket connection and attempting reconnect java.net.ConnectException: Connection refused at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717) at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:361) at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1081) [sample-dubbo-capital]2019-08-31 17:48:06,456 INFO [org.apache.zookeeper.ClientCnxn] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) [sample-dubbo-capital]2019-08-31 17:48:06,459 WARN [org.apache.zookeeper.ClientCnxn] Session 0x0 for server null, unexpected error, closing socket connection and attempting reconnect java.net.ConnectException: Connection refused at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717) at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:361) at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1081) [sample-dubbo-capital]2019-08-31 17:48:07,566 INFO [org.apache.zookeeper.ClientCnxn] Opening socket connection to server 127.0.0.1/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) [sample-dubbo-capital]2019-08-31 17:48:07,567 WARN [org.apache.zookeeper.ClientCnxn] Session 0x0 for server null, unexpected error, closing socket connection and attempting reconnect java.net.ConnectException: Connection refused 複製代碼

從報錯信息,一眼就看出是連不上zk即zookeeper。

這個很好理解,由於本地沒有安裝zk,因而安裝並經過"./zkServer.sh start"啓動zk

坑2:redis連不上

啓動tcc-transaction-dubbo-order報錯部分信息以下:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.mengyun.tcctransaction.sample.dubbo.order.service.PlaceOrderServiceImpl org.mengyun.tcctransaction.sample.dubbo.order.web.controller.OrderController.placeOrderService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'placeOrderServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.mengyun.tcctransaction.sample.dubbo.order.service.PaymentServiceImpl org.mengyun.tcctransaction.sample.dubbo.order.service.PlaceOrderServiceImpl.paymentService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.mengyun.tcctransaction.sample.dubbo.capital.api.CapitalTradeOrderService org.mengyun.tcctransaction.sample.dubbo.order.service.PaymentServiceImpl.capitalTradeOrderService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'captialTradeOrderService': Post-processing of FactoryBean's singleton object failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'compensableTransactionAspect' defined in class path resource [tcc-transaction.xml]: Cannot resolve reference to bean 'transactionConfigurator' while setting bean property 'transactionConfigurator'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionConfigurator': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.mengyun.tcctransaction.TransactionRepository org.mengyun.tcctransaction.spring.support.SpringTransactionConfigurator.transactionRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionRepository' defined in file [/Users/jackie/workspace/tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-dubbo-sample/tcc-transaction-dubbo-order/target/tcc-transaction-dubbo-order-1.2.6/WEB-INF/classes/config/spring/local/appcontext-service-tcc.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'jedisPool' threw exception; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:526)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:295)
	... 60 more
複製代碼

這個和上面的緣由相似,本地沒有安裝redis,致使沒法拿到redis鏈接。

因而安裝redis,並使用"redis-server"啓動redis。

坑3:Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection

三個項目都啓動後,能夠看到一個商品連接列表頁,可是在點擊連接後沒法跳轉,而且報錯以下

Type Exception Report
Message Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
### The error may exist in URL [jar:file:/Users/jackie/workspace/tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-http-sample/tcc-transaction-http-order/target/tcc-transaction-http-order-1.2.6/WEB-INF/lib/tcc-transaction-sample-order-1.2.6.jar!/config/sqlmap/main/sample-product.xml]
### The error may involve org.mengyun.tcctransaction.sample.order.infrastructure.dao.ProductDao.findByShopId
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)

Root Cause
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
### The error may exist in URL [jar:file:/Users/jackie/workspace/tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-http-sample/tcc-transaction-http-order/target/tcc-transaction-http-order-1.2.6/WEB-INF/lib/tcc-transaction-sample-order-1.2.6.jar!/config/sqlmap/main/sample-product.xml]
複製代碼

根據錯誤信息,排查是MySQL版本和數據庫驅動版本不匹配。

本地的MySQL版本是"8.0.11 MySQL Community Server - GPL",可是tcc-transaction中對應的驅動版本是

<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.33</version>
</dependency>
複製代碼

改成

<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.11</version>
</dependency>
複製代碼

同時針對高版本,須要在鏈接的jdbc-url後面加上useSSL=false

坑4:Loading class `com.mysql.jdbc.Driver'. This is deprecated

啓動tcc-transaction-dubbo-redpacket時,在日誌中看到一個警告"Loading class `com.mysql.jdbc.Driver'. This is deprecated"。

經過搜索,發現是由於數據庫驅動com.mysql.jdbc.Driver'已經被棄用了,須要使用com.mysql.cj.jdbc.Driver,因而修改jdbc.proerties的配置(具體配置見上面),啓動正常。

踩完上面的坑後,啓動三個項目,完整走完流程,實現了一個基於分佈式事務的商品購買行爲,具體過程以下圖所示

總結

運行示例項目的過程不算太順利,主要有一下幾個緣由吧

  • 本地環境配置和項目提供的不一致,致使走了不少彎路,好比MySQL的版本。
  • 缺乏詳細的跑示例項目的文檔說明。
  • 網上提供的資料比較粗略,也比較陳舊,文中能跑起來的步驟說明已經不適用如今的代碼了。

因此,在踩完這麼多坑總結下,避免後面的人走一樣的彎路。

我的公衆號JackieZheng,歡迎關注~

相關文章
相關標籤/搜索