1 业务说明
本案例通过hmily框架实现TCC分布式事务,模拟两个账户的转账交易过程。两个账户分别在不同的银行(张三在bank1、李四在bank2),bank1、bank2是两个微服务。对于交易过程中的每个操作,要么都 成功,要么都失败。
2 环境搭建
2.1 环境要求
数据库:MySQL 5.7.25+ JDK: jdk1.8+ 微服务:spring-boot-2.1.3、spring-cloud-Greenwich.RELEASE hmily:hmily-springcloud.2.0.4-RELEASE
2.2 数据库
创建bank1库,并导入以下表结构和数据:
CREATE DATABASE `bank1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; USE bank1; DROP TABLE IF EXISTS `account_info`; CREATE TABLE `account_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名', `account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号', `account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码', `account_balance` double NULL DEFAULT NULL COMMENT '帐户余额', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; INSERT INTO `account_info` VALUES (1, '张三', '1', '', 10000);
创建bank2库,并导入以下表结构和数据:
CREATE DATABASE `bank2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; USE bank2; DROP TABLE IF EXISTS `account_info`; CREATE TABLE `account_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `account_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '户主姓名', `account_no` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '银行卡号', `account_password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '帐户密码', `account_balance` double NULL DEFAULT NULL COMMENT '帐户余额', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic; INSERT INTO `account_info` VALUES (2, '李四', '2', NULL, 0);
Hmily用来存储日志的数据表由它自动创建
2.3 Maven工程
(1)搭建maven工程,以tcc-hmily-demo为父工程,进行依赖管理,hmily-demo-bank1负责张三账 户操作,hmily-demo-bank2负责李四账户操作,hmily-demo-discover-server是服务注册中心。
父级pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.itcast.wanxintx</groupId> <artifactId>tcc-hmily-demo</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>hmily-demo-bank1</module> <module>hmily-demo-bank2</module> <module>hmily-demo-discover-server</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.dromara</groupId> <artifactId>hmily-springcloud</artifactId> <version>2.0.4-RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.interceptor</groupId> <artifactId>javax.interceptor-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies> </dependencyManagement> <build> <finalName>${project.name}</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/*</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>utf-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> </configuration> </plugin> </plugins> </build> </project>
bank1
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>tcc-hmily-demo</artifactId> <groupId>cn.itcast.wanxintx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hmily-demo-bank1</artifactId> <dependencies> <dependency> <groupId>org.dromara</groupId> <artifactId>hmily-springcloud</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
bank2
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>tcc-hmily-demo</artifactId> <groupId>cn.itcast.wanxintx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hmily-demo-bank2</artifactId> <dependencies> <dependency> <groupId>org.dromara</groupId> <artifactId>hmily-springcloud</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
2.4 功能实现
2.4.1 hmily-demo-bank1工程
(1)application.yml配置
server: servlet: context-path: /bank1 port: 56081 eureka: instance: preferIpAddress: true instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}} lease-renewal-interval-in-seconds: 5 # 续约更新时间间隔(默认30秒) lease-expiration-duration-in-seconds: 10 # 续约到期时间(默认90秒) client: registry-fetch-interval-seconds: 5 # 抓取服务列表 serviceUrl: defaultZone: http://localhost:56080/eureka/ spring: application: name: hmily-demo-bank1 datasource: url: jdbc:mysql://localhost:3306/bank1?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=utf-8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver org: dromara: hmily : serializer : kryo #序列化工具 retryMax : 2 #最大重试次数 repositorySupport : db #持久化方式 started: true #事务发起方 hmilyDbConfig : driverClassName : com.mysql.jdbc.Driver url : jdbc:mysql://localhost:3306/bank1?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=utf-8 username : root password : root ribbon: ConnectTimeout: 60000 # 设置连接超时时间 default 2000 ReadTimeout: 60000 # 设置读取超时时间 default 5000 OkToRetryOnAllOperations: true # 对所有操作请求都进行重试 default false MaxAutoRetriesNextServer: 2 # 切换实例的重试次数 default 1 MaxAutoRetries: 1 # 对当前实例的重试次数 default 0
(2)Hmily配置类
package cn.itcast.wanxintx.hmilydemo.bank1.config; import org.dromara.hmily.common.config.HmilyDbConfig; import org.dromara.hmily.core.bootstrap.HmilyTransactionBootstrap; import org.dromara.hmily.core.service.HmilyInitService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.core.env.Environment; @Configuration @EnableAspectJAutoProxy(proxyTargetClass=true) public class DatabaseConfiguration { @Autowired private Environment env; @Bean public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){ HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService); hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer")); hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax"))); hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport")); hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started"))); HmilyDbConfig hmilyDbConfig = new HmilyDbConfig(); hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName")); hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url")); hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username")); hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password")); hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig); return hmilyTransactionBootstrap; } }
(3)feign代理
package cn.itcast.wanxintx.hmilydemo.bank1.feignClient; import org.dromara.hmily.annotation.Hmily; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(value = "hmily-demo-bank2") public interface Bank2Client { @GetMapping("/bank2/transfer") @Hmily Boolean transfer(@RequestParam("amount") Double amount); }
(4)转账业务
package cn.itcast.wanxintx.hmilydemo.bank1.service; import cn.itcast.wanxintx.hmilydemo.bank1.dao.AccountInfoDao; import cn.itcast.wanxintx.hmilydemo.bank1.feignClient.Bank2Client; import org.dromara.hmily.annotation.Hmily; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountInfoTccImpl implements AccountInfoTcc { @Autowired private AccountInfoDao accountInfoDao; @Autowired private Bank2Client bank2Client; @Override @Hmily(confirmMethod = "commit", cancelMethod = "rollback") public void prepare( String accountNo, double amount) { System.out.println("******** Bank1 Service prepare... " ); if(!bank2Client.transfer(amount)){ throw new RuntimeException("bank2 exception"); } } @Override public void commit( String accountNo, double amount) { System.out.println("******** Bank1 Service commit..." ); } @Override public void rollback( String accountNo, double amount) { accountInfoDao.updateAccountBalance(accountNo ,amount ); System.out.println("******** Bank1 Service rollback... " ); } }
(5)启动类
@SpringBootApplication(exclude = MongoAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients(basePackages = {"cn.itcast.wanxintx.hmilydemo.bank1.feignClient"}) @ComponentScan({"cn.itcast.wanxintx.hmilydemo.bank1","org.dromara.hmily"}) public class Bank1HmilyServer { public static void main(String[] args) { SpringApplication.run(Bank1HmilyServer.class, args); } }
2.4.2 hmily-demo-bank2工程
(1)application.yml配置
server: servlet: context-path: /bank2 port: 56082 eureka: instance: preferIpAddress: true instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}} lease-renewal-interval-in-seconds: 5 # 续约更新时间间隔(默认30秒) lease-expiration-duration-in-seconds: 10 # 续约到期时间(默认90秒) client: registry-fetch-interval-seconds: 5 # 抓取服务列表 serviceUrl: defaultZone: http://localhost:56080/eureka/ spring: application: name: hmily-demo-bank2 datasource: url: jdbc:mysql://localhost:3306/bank2?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=utf-8 username: root password: root type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver org: dromara: hmily : serializer : kryo #序列化工具 retryMax : 2 #最大重试次数 repositorySupport : db #持久化方式 started: false #事务参与方 hmilyDbConfig : driverClassName : com.mysql.jdbc.Driver url : jdbc:mysql://localhost:3306/bank2?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=utf-8 username : root password : root ribbon: ConnectTimeout: 600 # 设置连接超时时间 default 2000 ReadTimeout: 600 # 设置读取超时时间 default 5000 OkToRetryOnAllOperations: true # 对所有操作请求都进行重试 default false MaxAutoRetriesNextServer: 2 # 切换实例的重试次数 default 1 MaxAutoRetries: 1 # 对当前实例的重试次数 default 0
(2)Hmily配置类
package cn.itcast.wanxintx.hmilydemo.bank2.config; import com.alibaba.druid.pool.DruidDataSource; import org.dromara.hmily.common.config.HmilyDbConfig; import org.dromara.hmily.core.bootstrap.HmilyTransactionBootstrap; import org.dromara.hmily.core.service.HmilyInitService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.core.env.Environment; @Configuration @EnableAspectJAutoProxy(proxyTargetClass=true) public class DatabaseConfiguration { @Autowired private Environment env; @Bean public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){ HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService); hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer")); hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax"))); hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport")); hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started"))); HmilyDbConfig hmilyDbConfig = new HmilyDbConfig(); hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName")); hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url")); hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username")); hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password")); hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig); return hmilyTransactionBootstrap; } }
(3)feign代理
bank1 调用 bank2 这里没有
(4)转账业务
package cn.itcast.wanxintx.hmilydemo.bank2.service; import cn.itcast.wanxintx.hmilydemo.bank2.dao.AccountInfoDao; import org.dromara.hmily.annotation.Hmily; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountInfoServiceImpl implements AccountInfoService { @Autowired private AccountInfoDao accountInfoDao; @Override @Hmily(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod") public Boolean updateAccountBalance(String accountNo, Double amount) { System.out.println("******** Bank2 Service Begin ..."); try{ //accountInfoDao=null; //测试使用 accountInfoDao.updateAccountBalance(accountNo ,amount); }catch(Exception e){ throw new RuntimeException( e.getMessage() ); } return true; } public Boolean confirmMethod(String accountNo, Double amount) { System.out.println("******** Bank2 Service commit... " ); return true; } public Boolean cancelMethod(String accountNo, Double amount) { accountInfoDao.updateAccountBalance(accountNo ,amount * -1); System.out.println("******** Bank2 Service rollback... " ); return true; } }
(5)启动类
package cn.itcast.wanxintx.hmilydemo.bank2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication(exclude = MongoAutoConfiguration.class) @EnableDiscoveryClient @ComponentScan({"cn.itcast.wanxintx.hmilydemo.bank2","org.dromara.hmily"}) public class Bank2HmilyServer { public static void main(String[] args) { SpringApplication.run(Bank2HmilyServer.class, args); } }
注意:Try、Confirm、cancel的方法参数必须保持一致。形参哈
总结
功能测试 Bank1(张三)和Bank2(李四)都执行成功。
Bank2(李四)执行失败,Bank1(张三)事务回滚。
还是9900 成功回滚
转https://www.cnblogs.com/mangoubiubiu/p/15630968.html
标签:dromara,Hmily,spring,springframework,hmily,import,org,TCC From: https://www.cnblogs.com/smallfa/p/17129057.html