分布式事务--Hmily(feign) ﹏ヽ暗。殇╰゛Y 2022-11-01 05:56 220阅读 0赞 原文网址:[分布式事务--Hmily(feign)\_IT利刃出鞘的博客-CSDN博客][--Hmily_feign_IT_-CSDN] # 其他网址 # **官网** > 文档(springcloud):[SpringCloud用户指南 · dromara(Open source organization)][SpringCloud_ _ dromara_Open source organization] > 官网示例:[https://github.com/dromara/hmily/tree/master/hmily-demo/hmily-demo-springcloud][https_github.com_dromara_hmily_tree_master_hmily-demo_hmily-demo-springcloud] > [分布式事务解决方案之TCC(Hmily)\_篮战丶的博客-CSDN博客\_tcc分布式事务][TCC_Hmily_-CSDN_tcc] > [Hmily实现TCC事务控制\_传智燕青-CSDN博客][Hmily_TCC_-CSDN] > [Hmily:高性能异步分布式事务TCC框架\_架构师的成长之路的博客-CSDN博客][Hmily_TCC_-CSDN 1] > > [Hmily实现TCC事务 | 码农家园][Hmily_TCC_ _] # 简介 # 本文使用himily整合springcloud的feign测试TCC分布式事务。所有代码:[https://gitee.com/shapeless/demo\_SpringCloud/tree/hmily/][https_gitee.com_shapeless_demo_SpringCloud_tree_hmily] **业务场景** > 创建订单时,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框架保存回滚的信息。本处只需创建库即可,在运行时,它会自动创建表结构,创建后的表结构如下: > > ![20210226174349275.png][] **创建业务库和表** > 创建名为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: 222333 > > 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: 222333 > > 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][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][http_localhost_6001_order_order_createFault_userId_1_productId_1_count_10_money_100] [--Hmily_feign_IT_-CSDN]: https://knife.blog.csdn.net/article/details/114013213 [SpringCloud_ _ dromara_Open source organization]: https://dromara.org/zh/projects/hmily/user-springcloud/ [https_github.com_dromara_hmily_tree_master_hmily-demo_hmily-demo-springcloud]: https://github.com/dromara/hmily/tree/master/hmily-demo/hmily-demo-springcloud [TCC_Hmily_-CSDN_tcc]: https://blog.csdn.net/qq_34125999/article/details/109089260 [Hmily_TCC_-CSDN]: https://blog.csdn.net/weixin_44062339/article/details/100180025 [Hmily_TCC_-CSDN 1]: https://blog.csdn.net/qq_27384769/article/details/89508895 [Hmily_TCC_ _]: https://www.codenong.com/cs106852111/ [https_gitee.com_shapeless_demo_SpringCloud_tree_hmily]: https://gitee.com/shapeless/demo_SpringCloud/tree/hmily/ [20210226174349275.png]: /images/20221024/9861ef9b37cb47bc963e3d1ee30d5070.png [http_localhost_6001_order_order_createNormal_userId_1_productId_1_count_10_money_100]: http://localhost:6001/order/order/createNormal?userId=1&productId=1&count=10&money=100 [http_localhost_6001_order_order_createFault_userId_1_productId_1_count_10_money_100]: http://localhost:6001/order/order/createFault?userId=1&productId=1&count=10&money=100
相关 【分布式事务】分布式事务Seata 文章目录 前言 什么是分布式事务? (1)数据库分库分表就产生了分布式事务; (2)项目拆分服务化也产生了分布式事务; 一、W 客官°小女子只卖身不卖艺/ 2023年10月09日 13:49/ 0 赞/ 72 阅读
相关 分布式事务(二)分布式事务方案 文章目录 什么是分布式事务 理论部分 CAP BASE 分布式事务方案 什么是分布式事务 首先这是普通事务: ![ 太过爱你忘了你带给我的痛/ 2023年02月28日 01:29/ 0 赞/ 13 阅读
相关 分布式-分布式事务 分布式事务 文章目录 分布式事务 一,本地消息表 二,2PC 两阶段提交 三,3PC 三段式提交 四,T 待我称王封你为后i/ 2022年11月27日 15:40/ 0 赞/ 334 阅读
相关 分布式事务01-分布式事务概述 文章目录 1.分布式事务产生的背景 1.1 分布式事务在不同场景下如何产生 1.2 案例 2.事务理论知识 2.1 AC 旧城等待,/ 2022年04月25日 09:54/ 0 赞/ 397 阅读
相关 分布式事务 一、前言 在单个数据库实例时候,我们可以使用一个数据源的事务([本地事务][Link 1] )来保证事务内的多个操作要么全部执行生效,要么全部不生效。在多数据库实例节点时 冷不防/ 2022年04月14日 02:09/ 0 赞/ 412 阅读
相关 分布式事务 什么是分布式事务 分布式事务涉及到操作多个数据库的事务,分布式事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的`不同节点上`。一个分布式事务可 Dear 丶/ 2022年03月10日 15:27/ 0 赞/ 287 阅读
相关 分布式事务 1、分布式事务产生的背景 在微服务环境下,因为会根据不同的业务会拆分成不同的服务,比如会员服务、订单服务、商品服务等,让专业的人做专业的事情,每个服务都有自己独立的数据库,并 - 日理万妓/ 2021年10月25日 14:09/ 0 赞/ 493 阅读
相关 分布式事务 阅读: 深入理解分布式事务,高并发下分布式事务的解决方案 [https://blog.csdn.net/qq\_32534441/article/details/890 红太狼/ 2021年10月13日 01:13/ 0 赞/ 518 阅读
相关 分布式事务 在分布式系统中,为了保证数据的高可用,通常,我们会将数据保留多个副本(replica),这些副本会放置在不同的物理的机器上。为了对用户提供正确的 CRUD 等语义,我们需... 灰太狼/ 2020年04月24日 17:42/ 0 赞/ 990 阅读
还没有评论,来说两句吧...