springcloud -分布式事务解决方案 seata 分布式id生成方案

发布时间 2023-07-11 21:31:58作者: 你就学个JVAV?

 

使用三个服务来进行演示

三个服务的共同部分

 

pom相关依赖

 <!--nacos-->
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>
 <!--seata-->
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
     <exclusions>
         <exclusion>
             <artifactId>seata-all</artifactId>
             <groupId>io.seata</groupId>
         </exclusion>
     </exclusions>
 </dependency>
 ​
 // 1.0 之后的版本引入方式不一样
 // 1.0 之后需要使用
 // <artifactId>seata-spring-boot-start</artifactId>
 <dependency>
     <groupId>io.seata</groupId>
     <artifactId>seata-all</artifactId>
     <version>0.9.0</version>
 </dependency>
 <!--feign-->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>
  1. 取消数据源的自动创建

 @EnableDiscoveryClient
 @EnableFeignClients
 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建
 public class SeataOrderMainApp2001
 {
     public static void main(String[] args)
     {
         SpringApplication.run(SeataOrderMainApp2001.class, args);
     }
 }
  1. 使用seata对数据源进行代理

 @Configuration
 public class DataSourceProxyConfig {
 ​
     @Value("${mybatis.mapperLocations}")
     private String mapperLocations;
 ​
     @Bean
     @ConfigurationProperties(prefix = "spring.datasource")
     public DataSource druidDataSource(){
         return new DruidDataSource();
     }
 ​
     @Bean
     public DataSourceProxy dataSourceProxy(DataSource dataSource) {
         return new DataSourceProxy(dataSource);
     }
     @Bean
     public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
         sqlSessionFactoryBean.setDataSource(dataSourceProxy);
         sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
         sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
         return sqlSessionFactoryBean.getObject();
     }
 }
  1. mybatis的配置

 @Configuration
 @MapperScan({"com.atguigu.springcloud.alibaba.dao"})
 public class MyBatisConfig {
 }
  1. 下订单(order)服务controller部分

 @RestController
 public class OrderController
 {
     @Resource
     private OrderService orderService;
 ​
 ​
     @GetMapping("/order/create")
     public CommonResult create(Order order)
     {
         orderService.create(order);
         return new CommonResult(200,"订单创建成功");
     }
 }

  1. commresult.java

 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 public class CommonResult<T>
 {
     private Integer code;
     private String  message;
     private T       data;
 ​
     public CommonResult(Integer code, String message)
     {
         this(code,message,null);
     }
 }
  1. 订单部分service 关键注解 @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)

 @Service
 @Slf4j
 public class OrderServiceImpl implements OrderService
 {
     @Resource
     private OrderDao orderDao;
     @Resource
     private StorageService storageService;
     @Resource
     private AccountService accountService;
 ​
     /**
      * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
      * 简单说:下订单->扣库存->减余额->改状态
      */
     @Override
     // 关键注解,加上之后就能支持全局事务
     @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
     public void create(Order order)
     {
         log.info("----->开始新建订单");
         //1 新建订单
         orderDao.create(order);
 ​
         //2 扣减库存
         log.info("----->订单微服务开始调用库存,做扣减Count");
         storageService.decrease(order.getProductId(),order.getCount());
         log.info("----->订单微服务开始调用库存,做扣减end");
 ​
         //3 扣减账户
         log.info("----->订单微服务开始调用账户,做扣减Money");
         accountService.decrease(order.getUserId(),order.getMoney());
         log.info("----->订单微服务开始调用账户,做扣减end");
 ​
         //4 修改订单状态,从零到1,1代表已经完成
         log.info("----->修改订单状态开始");
         orderDao.update(order.getUserId(),0);
         log.info("----->修改订单状态结束");
 ​
         log.info("----->下订单结束了,O(∩_∩)O哈哈~");
 ​
     }
 }
  1. feign对另外两个服务发起远程调用

 // accountService.class
 @FeignClient(value = "seata-account-service")
 public interface AccountService
 {
     @PostMapping(value = "/account/decrease")
     CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
 }
 ​
 // storageService.class
 @FeignClient(value = "seata-storage-service")
 public interface StorageService
 {
     @PostMapping(value = "/storage/decrease")
     CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
 }

mapper.xml

 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 ​
 <mapper namespace="com.atguigu.springcloud.alibaba.dao.OrderDao">
 ​
     <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Order">
         <id column="id" property="id" jdbcType="BIGINT"/>
         <result column="user_id" property="userId" jdbcType="BIGINT"/>
         <result column="product_id" property="productId" jdbcType="BIGINT"/>
         <result column="count" property="count" jdbcType="INTEGER"/>
         <result column="money" property="money" jdbcType="DECIMAL"/>
         <result column="status" property="status" jdbcType="INTEGER"/>
     </resultMap>
 ​
     <insert id="create">
         insert into t_order (id,user_id,product_id,count,money,status)
         values (null,#{userId},#{productId},#{count},#{money},0);
     </insert>
     <update id="update">
         update t_order set status = 1
         where user_id=#{userId} and status = #{status};
     </update>
 ​
 </mapper>

resource 部分

 server:
   port: 2001
 ​
 spring:
   application:
     name: seata-order-service
   cloud:
     alibaba:
     #seata 的配置
       seata:
         #自定义事务组名称需要与seata-server中的对应
         tx-service-group: fsp_tx_group
     #nacos的配置
     nacos:
       discovery:
         server-addr: localhost:8848
   # 数据源的配置      
   datasource:
     driver-class-name: com.mysql.jdbc.Driver
     url: jdbc:mysql://192.168.1.4:3306/seata_order
     username: root
     password: root
 # 关闭hystrix
 feign:
   hystrix:
     enabled: false
 ​
 logging:
   level:
     io:
       seata: info
 # 指定mapper的位置扫描
 mybatis:
   mapperLocations: classpath:mapper/*.xml

file.conf相对原始文件需要修改的地方

 servcie部分 (此处为客户配置端)
 service {
     # fsp_tx_group是服务端配置的事务组名
     vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称
 }
 ​
 servcie部分 (此处为服务配置端)
 service {
     # fsp_tx_group是服务端配置的事务组名
     vgroup_mapping.my_test_tx_group. = "fsp_tx_group" #修改自定义事务组名称
 }
 ​
 registry部分
 registry {
       # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
       type = "nacos"  // 此处若使用nacos进行服务注册需要改成nacos
       nacos {
         serverAddr = "localhost:8848"
         namespace = ""
         cluster = "default"
       }
 }

 

雪花算法优缺点(snowflake)

解决了时钟回拨的两个算法

百度的和美团的两个id生成算法