1、背景

前段时间在看项目代码的时候,发现有些接口的流程比较长,在各个服务里面都有通过数据库事务保证数据的一致性,但是在上游的controller层并没有对一致性做保证。

网上查了下,还没找到基于Go开源的比较成熟的分布式事务框架。

于是,准备看看之前隔壁部门大佬写的tcc-transaction,这是一个基于tcc思想实现的分布式事务框架。

tcc分别代码Try,Confirm和Cancel。

Try: 尝试执行业务

  • 完成所有业务检查(一致性)
  • 预留必须业务资源(准隔离性)

Confirm: 确认执行业务

  • 真正执行业务
  • 不作任何业务检查
  • 只使用Try阶段预留的业务资源
  • Confirm操作满足幂等性

Cancel: 取消执行业务

  • 释放Try阶段预留的业务资源
  • Cancel操作满足幂等性

要了解其实现原理,第一步就是跑通项目自带的示例,即tcc-transaction-tutorial-sample部分的代码。

今天主要介绍在跑通tcc-transaction-tutorial-sample过程中遇到的各种坑。

2、依赖环境

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

源码地址:https://github.com/changmingxie/tcc-transaction

我自己Fork了一份(配置改动已提交):https://github.com/DMinerJackie/tcc-transaction

3、踩坑历程

踩坑准备

第一步:克隆代码

使用"git clone https://github.com/DMinerJackie/tcc-transaction"命令下载代码

第二步:导入代码并执行数据库脚本

代码导入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=
c3p0.minPoolSize=
c3p0.maxPoolSize=
c3p0.acquireIncrement=
c3p0.maxIdleTime=
c3p0.checkoutTimeout= 

同时修改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的配置(具体配置见上面),启动正常。

踩完上面的坑后,启动三个项目,完整走完流程,实现了一个基于分布式事务的商品购买行为,具体过程如下图所示

4、 总结

运行示例项目的过程不算太顺利,主要有一下几个原因吧

  • 本地环境配置和项目提供的不一致,导致走了很多弯路,比如MySQL的版本。
  • 缺少详细的跑示例项目的文档说明。
  • 网上提供的资料比较粗略,也比较陈旧,文中能跑起来的步骤说明已经不适用现在的代码了。

所以,在踩完这么多坑总结下,避免后面的人走同样的弯路。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

终于跑通分布式事务框架tcc-transaction的示例项目的更多相关文章

  1. tcc分布式事务框架解析

    前言碎语 楼主之前推荐过2pc的分布式事务框架LCN.今天来详细聊聊TCC事务协议. 2pc实现:https://github.com/codingapi/tx-lcn tcc实现:https://g ...

  2. 【原创】分布式事务之TCC事务模型

    引言 在上篇文章<老生常谈--利用消息队列处理分布式事务>一文中留了一个坑,今天来填坑.如下图所示 如果服务A和服务B之间是同步调用,比如服务C需要按流程调服务A和服务B,服务A和服务B要 ...

  3. TX-LCN 分布式事务框架

    第十章 TX-LCN 分布式事务框架 (Spring Cloud 高级) 一. 什么是分布式事务 分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位 于不同的分布式系统的不同 ...

  4. 快速了解阿里微服务热门开源分布式事务框架——Seata

    一.Seata 概述 Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而来. Seata 是 ...

  5. 分布式事务之TCC事务模型

    一.引言 在上篇文章<老生常谈--利用消息队列处理分布式事务>一文中留了一个坑,今天来填坑.如下图所示 如果服务A和服务B之间是同步调用,比如服务C需要按流程调服务A和服务B,服务A和服务 ...

  6. 基于Dubbo的分布式事务框架(LCN)

    原文地址:http://原文地址:https://github.com/1991wangliang/transaction 基于Dubbo的分布式事务框架(LCN) 该框架依赖Redis/dubbo/ ...

  7. 分布式事务框架Seata及EasyTransaction架构的比对思考

    本文将会对比Seata与EasyTransaction两个分布式事务的一些高层设计,相信大家会有收获. Seata的概述 Seata(曾用名Fescar,开源版本GTS)是阿里的开源分布式事务框架,其 ...

  8. 关于如何实现一个Saga分布式事务框架的思考

    关于Saga模式的介绍,已经有一篇文章介绍的很清楚了,链接在这里:分布式事务:Saga模式. 关于TCC模式的介绍,也已经有一篇文章介绍的很清楚了,链接在这里:关于如何实现一个TCC分布式事务框架的一 ...

  9. 开源的分布式事务框架 springcloud Alibaba Seata 的搭建使用 一次把坑踩完。。。

    seata的使用 1. Seata 概述 Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而 ...

随机推荐

  1. 如何使用 nvm-windows 管理 nodejs 版本

    写在前边的话: (1). 路径一定不要包含空格,如 Program Files 这样,所以有把程序安装到 D:\Program Files 文件下的同学请注意了:(2). 为了避免 nvm 无法切换源 ...

  2. Codeforces1144B(B题)Parity Alternated Deletions

    B. Parity Alternated Deletions Polycarp has an array aa consisting of nn integers. He wants to play ...

  3. Linux中bash shell环境变量

    别名 别名是命令的快捷方式.为那些需要经常执行,但需要很长时间输入的长命令创建快捷方式很有用.语法是: alias ppp='ping www.baidu.com' 它们并不总是用来缩短长命令.重要的 ...

  4. 1.4.2python网站地图爬虫(每天一更)

    # -*- coding: utf-8 -*- ''' Created on 2019年5月6日 @author: 薛卫卫 ''' import urllib.request import re de ...

  5. 提交bug的标准及书写规范

    Bug有效性 1.交付过程中测试者需按照专家设定好的模块,对Bug进行归类提交: 2.Bug的类型默认为UI问题.功能问题.崩溃问题,提交Bug时不能弄错: 3.需求是否明确.前提条件是否满足.输入数 ...

  6. 『开发技术』Windows极简安装使用face_recognition

    face_recognition是一个强大.简单.易上手的人脸识别开源项目,并且配备了完整的开发文档和应用案例,特别是兼容树莓派系统.此项目是世界上最简洁的人脸识别库,你可以使用Python和命令行工 ...

  7. Jenkins 持续集成持续发布使用搭建基础

    一.环境搭建基础 1.持续集成.持续交付.持续部署概念 ①.集成: 是指软件多人研发的部分软件代码整合交付,以便尽早发现个人开发部分的问题:持续集成:强调开发人员提交了新代码之后,立刻进行构建(单元) ...

  8. 0R电阻在PCB布线中对布线畅通的一个小妙用

    在PCB布线中,我们都会尽量节约板子空间,将元器件排布的紧密一些,难免会遇到布线不通的时候. 博主下面就来说一个关于0R电阻在PCB布线使之畅通的一个小妙用. 使用0R电阻前 假设我们这个TXD的线周 ...

  9. eclipse导入码云-GIT项目

    1.首先找到项目源码地址我随便找到一个git地址 :https://gitee.com/mingSoft/MCMS 2.打开eclipse空白处右键导入项目搜索git. 3.将第一步复制的git地址复 ...

  10. JavaWeb配置详解(结合框架SpringMVC)

    详解 先说一说常识性的东西,我们的JavaWeb程序运行一开始走的是web.xml文件,这是我们的核心文件,可以说没有web.xml文件我们就无法运行项目,这个文件长什么样子,读者自己新建一个web项 ...