在互联网大厂的项目开发中,多个微服务之间的业务操作需要保证数据的一致性,比如电商系统中的下单与库存扣减,传统的本地事务在分布式环境下完全失效,数据不一致的问题频发,严重影响系统的稳定性和用户体验,让人焦头烂额!
随着互联网业务规模的不断扩大,分布式系统已经成为互联网大厂的主流架构。在这种架构下,一个业务操作往往会涉及到多个服务和数据库,而传统的本地事务无法满足分布式环境下的数据一致性要求。以电商场景为例,用户下单后,订单服务要记录订单信息,库存服务要扣减库存,支付服务要处理支付流程,任何一个环节出错,如果没有有效的事务管理,就会导致数据混乱,如用户下单成功但库存未扣减,或者支付成功但订单未生成等问题。
Spring Boot3 是目前 Java 开发者常用的快速开发框架,它简化了 Spring 应用的搭建和开发过程,提升了开发效率。Seata 则是一款优秀的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。在 Seata 开源之前,其对应的内部版本在阿里内部一直扮演着分布式一致性中间件的角色,有力地支撑了阿里历年的双 11 大促等关键业务场景。将 Spring Boot3 与 Seata 进行整合,成为了解决分布式事务问题的有效途径。
接下来,就为大家详细介绍 Spring Boot3 中如何整合 Seata 实现分布式事务操作,以下是具体实现步骤:
创建 Spring Boot3 项目
使用 Spring Initializr(https://start.spring.io/ )创建多个 Spring Boot3 模块,例如订单服务、库存服务、支付服务等,每个模块对应一个微服务。在创建项目时,选择合适的依赖,如 Spring Web 等基础依赖,方便后续开发业务逻辑。
添加依赖
在项目的pom.xml文件中添加依赖。首先,在父 POM 中定义
spring-cloud-alibaba-dependencies依赖的版本,例如:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后在每个子模块中添加 Seata 相关依赖,如:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
同时,根据业务需求添加其他必要的依赖,如数据库连接依赖等。
配置 Nacos 与 Seata
Nacos 配置:启动 Nacos 服务,在 Nacos 配置列表项的 public 命名空间中,新增配置文件 "seataServer.properties"。在该文件中配置 Seata 服务相关参数,如:
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=true
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.serialization=seata
transport.compressor=none
Seata 配置:在每个 Spring Boot3 微服务的application.yml文件中,配置 Seata 相关信息,例如:
spring:
cloud:
alibaba:
seata:
tx-service-group: order-seata-service-group
seata:
service:
vgroup-mapping:
order-seata-service-group: default
config:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace:
registry:
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
group: SEATA_GROUP
namespace:
这里的tx-service-group定义了事务分组名称,vgroup-mapping将事务分组与 Seata 服务的集群名称进行映射,config.type和registry.type指定了配置和注册中心使用 Nacos。
数据源代理与undo_log表配置
数据源代理:在每个微服务中,配置
io.seata.rm.datasource.DataSourceProxy的 Bean,并将其设置为@Primary默认的数据源。例如:
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DataSourceProxy dataSourceProxy) {
return new DataSourceTransactionManager(dataSourceProxy);
}
}
创建undo_log表:在每个数据库中执行以下 SQL 语句创建undo_log表:
CREATE TABLE `undo_log` (
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='AT transaction mode undo table';
编写业务代码
在各个微服务中编写业务逻辑代码,并在需要开启分布式事务的方法上添加@GlobalTransactional注解。例如,在订单服务的下单方法上添加注解:
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@GlobalTransactional
@Transactional
public void placeOrder(Order order) {
// 保存订单信息
orderRepository.save(order);
// 调用库存服务扣减库存
inventoryService.reduceInventory(order.getProductId(), order.getQuantity());
// 调用支付服务处理支付
paymentService.processPayment(order.getOrderId(), order.getTotalAmount());
}
}
启动测试
依次启动 Nacos 服务、Seata 服务以及各个 Spring Boot3 微服务,然后模拟业务操作,如发起下单请求,检查分布式事务是否正常工作,即各个服务的操作是否符合事务一致性要求,若出现异常,事务是否能正确回滚。
然后是配置调整,这一步至关重要且较为复杂。在 Nacos 配置列表项的 public 命名空间中,我们要新增配置文件 "seataServer.properties"。在这个文件里,需要配置一系列关键参数,如transport.serialization=seata,它决定了数据传输时的序列化方式,采用 Seata 自身的序列化机制能更好地适配其分布式事务处理流程;transport.compressor=none则设置了数据传输的压缩方式,这里选择不压缩,开发者可根据实际网络环境和数据量大小进行调整。除了这些,还有众多与事务协调器(TC)、事务管理器(TM)、资源管理器(RM)相关的配置参数,例如配置 TC 的地址和端口,以确保各个服务能够准确地与 TC 进行通信,实现事务的协调管理。
在 Seata 体系中,事务协调器(TC)负责维护全局事务和分支事务的状态,驱动全局提交或回滚,它就像是分布式事务的指挥官;事务管理器(TM)用于定义全局事务的范围,包括开始全局事务、提交或回滚全局事务,是事务操作的发起者;资源管理器(RM)管理分支事务处理的资源,与 TC 交互以注册分支事务并报告其状态,同时驱动分支事务提交或回滚,是具体事务操作的执行者。当一个分布式事务开始时,TM 向 TC 申请开启一个全局事务,TC 创建全局事务并返回一个唯一的 XID,这个 XID 就像事务的身份证,贯穿整个分布式事务的处理流程。随后,各个服务中的 RM 向 TC 注册分支事务,并将其纳入 XID 对应的全局事务管辖中。例如在一个涉及订单服务、库存服务和支付服务的分布式事务中,这三个服务的 RM 都要向 TC 注册各自的分支事务。
此外,还需要进行数据源代理配置。Seata 是通过代理数据源实现事务分支的,所以需要配置
io.seata.rm.datasource.DataSourceProxy的 Bean,并且要将其设置为@Primary默认的数据源,否则事务不会回滚,无法实现分布式事务。同时,还需要在每个数据库中创建undo_log表,这是 Seata AT 模式所必需的。Seata AT 模式基于支持本地 ACID 事务的关系型数据库,Java 应用通过 JDBC 访问数据库。在一阶段,业务数据和回滚日志记录在同一个本地事务中提交,然后释放本地锁和连接资源;二阶段提交异步化,能非常快速地完成,若需要回滚,则通过一阶段的回滚日志进行反向补偿。
事务分组配置也不容忽视。在配置文件中,需要合理设置事务分组相关参数,如
service.vgroupMapping.order-seata-service-group=default,事务分组的合理设置有助于提高事务管理的效率和稳定性,不同的业务场景可以根据需求划分到不同的事务分组中。
每一个步骤都紧密相连且至关重要,稍有不慎就可能导致整合失败 。例如,如果依赖版本不匹配,可能会出现类冲突等问题,导致程序无法正常启动;配置参数错误,可能使得服务无法与 TC 建立连接,从而无法实现分布式事务管理;数据源代理配置不正确,事务回滚机制将失效,数据一致性无法保障。
总结
总结一下,在互联网大厂开发中,Spring Boot3 整合 Seata 实现分布式事务操作,能够有效保障分布式系统中数据的一致性,提升系统的稳定性和可靠性。各位开发同仁们,不妨在自己的项目中尝试一下这种解决方案,如果在整合过程中遇到了什么问题,欢迎在评论区留言讨论,大家一起攻克难题,让我们的项目更加健壮!