分布式事务解决方案_分布式事务框架

(3) 2024-06-08 11:23

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
分布式事务解决方案_分布式事务框架,希望能够帮助你!!!。

原文网址:分布式事务--Hmily(feign)_IT利刃出鞘的博客-CSDN博客

简介

说明

本文使用himily整合springcloud的feign测试TCC分布式事务。所有代码:https://gitee.com/shapeless/demo_SpringCloud/tree/hmily/

官网

文档(springcloud):SpringCloud用户指南 · dromara(Open source organization)
官网示例:https://github.com/dromara/hmily/tree/master/hmily-demo/hmily-demo-tcc/hmily-demo-tcc-springcloud

业务场景

创建订单时,order微服务先预生成订单(订单状态为创建中),再调用storage的feign来减库存,再调用account的feign来减账户余额,最后将订单状态改为已完成。

技术框架 

所用技术栈:

  • hmily-spring-boot-starter-springcloud:2.1.1
  • springboot:2.3.7.RELEASE
  • springcloud:Hoxton.SR9
  • mysql
  • mybatis-plus-boot-starter:3.4.1
  • eureka
  • gateway

所用插件:

  • lombok

技术细节 

配置方式

  • hmily配置: 各个微服务的resources路径:hmily.yml
  • 业务微服务服务:application.yml

缺陷

    存在的问题:order微服务只调用storage的feign是正常的,但若在后边加上account的feign,会报空指针异常。

建库建表

创建hmily数据库

创建名为hmily的数据库。

用于hmily框架保存回滚的信息。本处只需创建库即可,在运行时,它会自动创建表结构,创建后的表结构如下:

分布式事务解决方案_分布式事务框架_https://bianchenghao6.com/blog__第1张

创建业务库和表

创建名为business的数据库。

创建业务表:

CREATE TABLE `t_order` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `user_id` bigint(11) DEFAULT NULL COMMENT '用户id', `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `count` int(11) DEFAULT NULL COMMENT '数量', `money` decimal(11,0) DEFAULT NULL COMMENT '金额', `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结', `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0), PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; CREATE TABLE `t_account` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `user_id` bigint(11) DEFAULT NULL COMMENT '用户id', `total` decimal(10,0) DEFAULT NULL COMMENT '总额度', `used` decimal(10,0) DEFAULT NULL COMMENT '已用余额', `residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用额度', `frozen` decimal(10, 0) NULL DEFAULT NULL COMMENT '冻结金额', `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0), PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; # INSERT INTO `seat-account`.`account` (`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '100'); INSERT INTO `t_account` (`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '1000'); CREATE TABLE `t_storage` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `product_id` bigint(11) DEFAULT NULL COMMENT '产品id', `total` int(11) DEFAULT NULL COMMENT '总库存', `used` int(11) DEFAULT NULL COMMENT '已用库存', `residue` int(11) DEFAULT NULL COMMENT '剩余库存', `frozen` decimal(10, 0) NULL DEFAULT NULL COMMENT '冻结库存', `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0), PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; # INSERT INTO `seat-storage`.`storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1', '1', '100', '0', '100'); INSERT INTO `t_storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1', '1', '100', '0', '100');

业务微服务配置hmily

说明

hmily不需要像seata一样单独起一个服务。

本处只展示order微服务的hmily配置,其他微服务配置类似。只这几项不同:hmily.server.appName, hmily.config.appName, metrics.port

hmily.yml

hmily: server: configMode: local appName: appName_order # 如果server.configMode eq local 的时候才会读取到这里的配置信息. config: appName: appName_order repository: mysql ribbon: rule: enabled: true repository: database: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: root password:  metrics: metricsName: prometheus port: 8801

业务主要代码

order

controller

package com.example.order.controller; import com.example.order.entity.Order; import com.example.order.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { @Autowired OrderService orderService; @PostMapping("createNormal") public String createNormal(Order order) { orderService.createNormal(order); return "success"; } @PostMapping("createFault") public String createFault(Order order) { orderService.createFault(order); return "success"; } } 

service

Service

package com.example.order.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.order.entity.Order; public interface OrderService extends IService<Order> { void createNormal(Order order); void createFault(Order order); } 

ServiceImpl

package com.example.order.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.base.feign.AccountFeignClient; import com.example.base.feign.StorageFeignClient; import com.example.order.entity.Order; import com.example.order.mapper.OrderMapper; import com.example.order.service.OrderService; import com.example.order.tcc.OrderTccAction; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService { @Autowired StorageFeignClient storageFeignClient; @Autowired AccountFeignClient accountFeignClient; @Autowired OrderMapper orderMapper; @Autowired OrderTccAction orderTccAction; @Override public void createNormal(Order order) { log.info("创建订单总业务:开始"); save(order); orderMapper.updateStatus(order.getId(), 1); orderTccAction.tryCreateOrderNormal(order.getId(), order.getProductId(), order.getCount(), order.getUserId(), order.getMoney()); // ResultHolder.setResult(OrderTccAction.class, UUID.randomUUID().toString(), "p"); log.info("创建订单总业务:结束"); } @Override public void createFault(Order order) { log.info("创建订单总业务:开始"); save(order); orderMapper.updateStatus(order.getId(), 1); // orderTccAction.tryCreateOrderFault(order.getId(), order.getProductId(), order.getCount(), order.getUserId(), order.getMoney()); log.info("创建订单总业务:结束"); } } 

mapper

package com.example.order.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.order.entity.Order; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Repository public interface OrderMapper extends BaseMapper<Order> { @Update("UPDATE `t_order` SET status = #{status} WHERE id = #{id}") int updateStatus(@Param("id") Long id, @Param("status") Integer status); } 

storage

controller

package com.example.storage.feign; import com.example.storage.service.StorageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class FeignController { @Autowired StorageService storageService; @PostMapping("/feign/storage/tryDecreaseStorage") public boolean tryDecreaseStorage(@RequestParam("productId")Long productId, @RequestParam("count")Integer count) { storageService.tryDecreaseStorage(productId, count); return true; } } 

service

Service 

package com.example.storage.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.storage.entity.Storage; public interface StorageService extends IService<Storage> { void tryDecreaseStorage(Long productId, Integer count); } 

ServiceImpl

package com.example.storage.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.storage.entity.Storage; import com.example.storage.mapper.StorageMapper; import com.example.storage.service.StorageService; import lombok.extern.slf4j.Slf4j; import org.dromara.hmily.annotation.HmilyTCC; import org.springframework.stereotype.Service; @Service @Slf4j public class StorageServiceImpl extends ServiceImpl<StorageMapper, Storage> implements StorageService { @Override @HmilyTCC(confirmMethod = "confirmDecreaseStorage", cancelMethod = "cancelDecreaseStorage") public void tryDecreaseStorage(Long productId, Integer count) { log.info("库存服务:第一阶段开始:检查剩余库存、冻结库存"); Storage storage = getById(productId); if (storage.getResidue() < count) { throw new RuntimeException("库存不足。" + "实际库存:" + storage.getResidue() + ",购买数量:" + count); } getBaseMapper().prepareDecreaseStorage(productId, count); //第一阶段成功 添加一个标识。 //xid也可以通过RootContext.getXid()来获得 // ResultHolder.setResult(StorageFeignClient.class, RootContext.getXID(), "p"); // log.info("storage XID:{}", RootContext.getXID()); log.info("库存服务:第一阶段结束:检查剩余库存、冻结库存"); } public void confirmDecreaseStorage(Long productId, Integer count) { log.info("库存服务:第二阶段提交开始:释放冻结的库存、增加已用库存"); //TODO ResultHolder相关方法 getBaseMapper().commitDecreaseStorage(productId, count); log.info("库存服务:第二阶段提交结束:释放冻结的库存、增加已用库存"); } public void cancelDecreaseStorage(Long productId, Integer count) { log.info("库存服务:第二阶段回滚开始:释放冻结的库存、增加回原来剩余库存"); //TODO ResultHolder相关方法 getBaseMapper().cancelDecreaseStorage(productId, count); log.info("库存服务:第二阶段回滚结束:释放冻结的库存、增加回原来剩余库存"); } } 

mapper

package com.example.storage.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.storage.entity.Storage; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; @Repository public interface StorageMapper extends BaseMapper<Storage> { @Update("UPDATE `t_storage` SET used= used + #{count}, residue = residue - #{count} WHERE product_id = #{productId}") int decreaseStorage(@Param("productId") Long productId, @Param("count") Integer count); @Update("UPDATE `t_storage` SET frozen = frozen + #{count}, residue = residue - #{count} WHERE product_id = #{productId}") void prepareDecreaseStorage(@Param("productId") Long productId, @Param("count") Integer count); @Update("UPDATE `t_storage` SET frozen = frozen - #{count}, used = used + #{count} WHERE product_id = #{productId}") void commitDecreaseStorage(@Param("productId") Long productId, @Param("count") Integer count); @Update("UPDATE `t_storage` SET frozen = frozen - #{count}, residue = residue + #{count} WHERE product_id = #{productId}") void cancelDecreaseStorage(@Param("productId") Long productId, @Param("count") Integer count); } 

account

controller

package com.example.account.feign; import com.example.account.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController public class FeignController { @Autowired AccountService accountService; @PostMapping("/feign/account/tryDecreaseMoney") public void tryDecreaseMoney(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money) { accountService.tryDecreaseMoney(userId, money); } @PostMapping("/feign/account/tryDecreaseMoneyFault") public void tryDecreaseMoneyFault(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money) { accountService.tryDecreaseMoneyFault(userId, money); } } 

service

Service 

package com.example.account.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.account.entity.Account; import java.math.BigDecimal; public interface AccountService extends IService<Account> { void tryDecreaseMoney(Long userId, BigDecimal money); void tryDecreaseMoneyFault(Long userId, BigDecimal money); } 

ServiceImpl

package com.example.account.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.account.entity.Account; import com.example.account.mapper.AccountMapper; import com.example.account.service.AccountService; import lombok.extern.slf4j.Slf4j; import org.dromara.hmily.annotation.HmilyTCC; import org.springframework.stereotype.Service; import java.math.BigDecimal; @Service @Slf4j public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService { @Override @HmilyTCC(confirmMethod = "confirmDecreaseMoney", cancelMethod = "cancelDecreaseMoney") public void tryDecreaseMoney(Long userId, BigDecimal money) { log.info("账户服务:第一阶段开始:检查余额、冻结金额"); Account account = getById(userId); if (account.getResidue().compareTo(money) < 0) { throw new RuntimeException("余额不足。" + "实际余额:" + account.getResidue() + ",购买金额:" + money); } getBaseMapper().tryDecreaseMoney(userId, money); log.info("账户服务:第一阶段结束:检查余额、冻结金额"); } public void confirmDecreaseMoney(Long userId, BigDecimal money) { log.info("账户服务:第二阶段提交开始:释放冻结的金额、增加已用金额"); getBaseMapper().confirmDecreaseMoney(userId, money); log.info("账户服务:第二阶段提交结束:释放冻结的金额、增加已用金额"); } public void cancelDecreaseMoney(Long userId, BigDecimal money) { log.info("账户服务:第二阶段回滚开始:释放冻结的金额、增加回原来余额"); getBaseMapper().cancelDecreaseMoney(userId, money); log.info("账户服务:第二阶段回滚结束:释放冻结的金额、增加回原来余额"); } @Override @HmilyTCC(confirmMethod = "confirmDecreaseMoneyFault", cancelMethod = "cancelDecreaseMoneyFault") public void tryDecreaseMoneyFault(Long userId, BigDecimal money) { log.info("账户服务:第一阶段开始:检查余额、冻结金额"); Account account = getById(userId); if (account.getResidue().compareTo(money) < 0) { throw new RuntimeException("余额不足。" + "实际余额:" + account.getResidue() + ",购买金额:" + money); } getBaseMapper().tryDecreaseMoney(userId, money); log.info("账户服务:第一阶段结束:检查余额、冻结金额"); } public void confirmDecreaseMoneyFault(Long userId, BigDecimal money) { log.info("账户服务:第二阶段提交开始:释放冻结的金额、增加已用金额"); int i = 1 / 0; getBaseMapper().confirmDecreaseMoney(userId, money); log.info("账户服务:第二阶段提交结束:释放冻结的金额、增加已用金额"); } public void cancelDecreaseMoneyFault(Long userId, BigDecimal money) { log.info("账户服务:第二阶段回滚开始:释放冻结的金额、增加回原来余额"); int i = 1 / 0; getBaseMapper().cancelDecreaseMoney(userId, money); log.info("账户服务:第二阶段回滚结束:释放冻结的金额、增加回原来余额"); } } 

mapper

package com.example.account.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.account.entity.Account; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.RequestParam; import java.math.BigDecimal; @Repository public interface AccountMapper extends BaseMapper<Account> { @Update("UPDATE `t_account` SET residue = residue - #{money},used = used + #{money} where user_id = #{userId}") int decreaseMoney(@Param("userId")Long userId, @Param("money") BigDecimal money); @Update("UPDATE `t_account` SET frozen = frozen + #{money}, residue = residue - #{money} WHERE user_id = #{userId}") void tryDecreaseMoney(@Param("userId") Long userId, @Param("money") BigDecimal money); @Update("UPDATE `t_account` SET frozen = frozen - #{money}, used = used + #{money} WHERE user_id = #{userId}") void confirmDecreaseMoney(@Param("userId") Long userId, @Param("money") BigDecimal money); @Update("UPDATE `t_account` SET frozen = frozen - #{money}, residue = residue + #{money} WHERE user_id = #{userId}") void cancelDecreaseMoney(@Param("userId") Long userId, @Param("money") BigDecimal money); } 

业务次要代码

base

通用应用注解

package com.example.base.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @MapperScan("com.example.**.mapper") @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients("com.example.base.feign") public @interface BaseApplication { @AliasFor(annotation = SpringBootApplication.class) String[] scanBasePackages() default "com.example.**"; // @AliasFor(annotation = SpringBootApplication.class) // Class<?>[] exclude() default {DataSourceAutoConfiguration.class}; } 

mybatis-plus 自动填充插件

package com.example.base.config; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.LocalDateTime; @Configuration //@MapperScan("com.example.**.mapper") public class MyBatisPlusConfig { private static final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); //自动填充插件 @Bean public MetaObjectHandler metaObjectHandler() { return new MybatisPlusAutoFillConfig(); } public class MybatisPlusAutoFillConfig implements MetaObjectHandler { // 新增时填充 @Override public void insertFill(MetaObject metaObject) { logger.info("插入:自动填充createTime和updateTime:"+ LocalDateTime.now()); setFieldValByName("createTime", LocalDateTime.now(), metaObject); setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } // 修改时填充 @Override public void updateFill(MetaObject metaObject) { logger.info("更新:自动填充updateTime:" + LocalDateTime.now()); setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } } }

feign定义

package com.example.base.feign; import org.dromara.hmily.annotation.Hmily; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient("storage") public interface StorageFeignClient { @Hmily @PostMapping("/feign/storage/tryDecreaseStorage") void tryDecreaseStorage(@RequestParam("productId") Long productId, @RequestParam("count") Integer count); } 
package com.example.base.feign; import org.dromara.hmily.annotation.Hmily; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import java.math.BigDecimal; @FeignClient("account") public interface AccountFeignClient { @Hmily @PostMapping("/feign/account/tryDecreaseMoney") boolean tryDecreaseMoney(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money); @Hmily @PostMapping("/feign/account/tryDecreaseMoneyFault") boolean tryDecreaseMoneyFault(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money); } 

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.7.RELEASE</version> </parent> <packaging>pom</packaging> <groupId>com.example</groupId> <artifactId>base</artifactId> <version>0.0.1-SNAPSHOT</version> <name>base</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.6.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <!-- <version>8.0.21</version> 版本Spring-Boot-Parent中已带 --> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>hmily-spring-boot-starter-springcloud</artifactId> <version>2.1.1</version> <!-- <version>2.0.6-RELEASE</version>--> <exclusions> <exclusion> <groupId>org.dromara</groupId> <artifactId>hmily-repository-mongodb</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <!-- <version>Finchley.RELEASE</version>--> <!-- <version>Greenwich.SR3</version>--> <version>Hoxton.SR9</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>add-source</id> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>src/main/java</source> <source>../base</source> <!-- <source>../base/src/main/java</source>--> </sources> </configuration> </execution> </executions> </plugin> </plugins> <!-- <resources>--> <!-- <resource>--> <!-- <directory>src/main/resources</directory>--> <!-- <includes>--> <!-- <include>**/*</include>--> <!-- </includes>--> <!-- <filtering>false</filtering>--> <!-- </resource>--> <!-- <resource>--> <!-- <directory>../base/src/main/resources</directory>--> <!-- <includes>--> <!-- <include>**/*</include>--> <!-- </includes>--> <!-- <filtering>false</filtering>--> <!-- </resource>--> <!-- </resources>--> </build> </project> 

eureka/gateway

eureka

启动类

package com.example.eurekaserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } 

application.yml

server: port: 7001 spring: application: name: eureka-server eureka: instance: hostname: localhost1 client: register-with-eureka: false fetch-registry: false serviceUrl: defaultZone: http://localhost:7001/eureka/

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <!-- <version>2.0.6.RELEASE</version>--> <version>2.1.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <!-- <version>Finchley.SR2</version>--> <version>Greenwich.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project> 

gateway

启动类

package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } 

application.yml

server: port: 6001 spring: application: name: gateway cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true httpclient: ssl: use-insecure-trust-manager: true eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ # 配置Gateway日志等级,输出转发细节信息 logging: level: org.springframework.cloud.gateway: debug 

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gateway</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

order/storage/account

说明:order/storage/account的次要代码都是一样的。只是改了唯一性的东西:spring.applicaion.name、端口号、pom.xml的artifactId以及name。

启动类

package com.example.order; import com.example.base.config.BaseApplication; import org.springframework.boot.SpringApplication; @BaseApplication public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } } 

application.yml

server: port: 9011 spring: application: name: order datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/business?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: root password:  eureka: client: service-Url: defaultZone: http://localhost:7001/eureka # defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka feign: hystrix: enabled: false #Ribbon的负载均衡策略 #ribbon: # OkToRetryOnAllOperations: fasle # 对所有操作请求都进行重试 default false # MaxAutoRetriesNextServer: 0 # 切换实例的重试次数 default 1 # MaxAutoRetries: 0 # 对当前实例的重试次数 default 0 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #management: # metrics: # binders: # hystrix: # enabled: false 

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.example</groupId> <artifactId>base</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../base/pom.xml</relativePath> </parent> <artifactId>order</artifactId> <version>0.0.1-SNAPSHOT</version> <name>order</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> </dependencies> </project> 

测试

测试不报错的接口

访问:http://localhost:6001/order/order/createNormal?userId=1&productId=1&count=10&money=100

测试account服务报错的接口

访问:http://localhost:6001/order/order/createFault?userId=1&productId=1&count=10&money=100

其他网址

分布式事务解决方案之TCC(Hmily)_篮战丶的博客-CSDN博客_tcc分布式事务
Hmily实现TCC事务控制_传智燕青-CSDN博客
Hmily:高性能异步分布式事务TCC框架_架构师的成长之路的博客-CSDN博客

Hmily实现TCC事务 | 码农家园

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复