实战项目中整合Seata编码实践

第一步,创建 undo_log 表。

在需要处理分布式事务的微服务实例下的数据库中创建 undo_log 表,实战项目中涉及分布式事务处理的微服务实例有 订单微服务购物车微服务商品微服务,因此要在 newbee_mall_cloud_order_db 数据库、newbee_mall_cloud_cart_db 数据库和 newbee_mall_cloud_goods_db 数据库中依次创建 undo_log 表。

第二步,添加 Seata 依赖。

依次打开 newbee-mall-cloud-order-webnewbee-mall-cloud-shop-cart-webnewbee-mall-cloud-goods-web 3 个项目中的 pom.xml 文件,在 dependencies 节点下新增 Seata 的依赖项,配置代码如下:

<!-- Seata 依赖包 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

第三步,添加 Seata 配置项。

依次打开 newbee-mall-cloud-order-webnewbee-mall-cloud-shop-cart-webnewbee-mall-cloud-goods-web 3 个项目中的 application.properties 配置文件并进行修改,最终增加的配置项代码如下:

seata.enabled=true
#3个不同的微服务被命名为不同的名称,如goods-server、order-server、shopcart-server
seata.application-id=order-server
#事务分组配置
seata.tx-service-group=newbee_cloud_save_order_group
service.vgroupMapping.newbee_cloud_save_order_group=default
#连接Nacos服务中心的配置信息
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
seata.registry.nacos.group=DEFAULT_GROUP
seata.registry.nacos.cluster=default

在 3 个项目的配置文件中依次添加上述配置代码即可,其他配置项可以不用配置,使用 Seata 的默认值即可。

第四步,添加 Seata 数据源代理。

依次打开 newbee-mall-cloud-order-webnewbee-mall-cloud-shop-cart-webnewbee-mall-cloud-goods-web 3 个项目中的 config 包,新增 SeataProxyConfiguration 类,代码如下:

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

@Configuration
public class SeataProxyConfiguration {

    //创建 Druid 数据源
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    //创建 DataSource 数据源代理
    @Bean("dataSource")
    @Primary
    public DataSource dataSourceDelegation(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    /*
     * 解决 druid 日志报错; discard long time none received connection:xxx
     */
    @PostConstruct
    public void setProperties() {
        System.setProperty("druid.mysql.usePingMethod", "false");
    }
}

第五步,添加 @GlobalTransactional 注解。

打开 newbee-mall-cloud-order-web 项目中的 ltd.order.cloud.newbee.service.impl.NewBeeMallOrderServiceImpl 类,在 saveOrder() 方法上添加 @GlobalTransactional 注解,代码修改如下:

@Override
@Transactional
//加上这个注解,开启 Seata 分布式事务
@GlobalTransactional
public String saveOrder(Long mallUserId, MallUserAddress address, List<Long> cartItemIds) {
    //省略部分代码
}

saveOrder() 方法是一个涉及分布式事务的方法,在这个方法中会调用其他微服务来共同完成 “下单” 的流程,进而会操作 3 个独立的数据库。在这个方法上添加的 @GlobalTransactional 注解是全局事务注解,作用是开启全局事务。当执行到 saveOrder() 方法时,会自动开启全局事务。如果该方法中的代码逻辑都正常执行,则进行全局事务的 Commit 操作;如果该方法中抛出异常,则进行 RollBack 操作。在购物车微服务和商品微服务的分支事务中不需要添加这个注解。