概述
项目框架:Spring Framwork(4.3.10.RELEASE) + Dubbo(2.5.6)+ Mybatis Plus(2.3.3)
数据库:Postgresql
涉及的项目模块分为:
app-webservice(对外提供接口)
base(存放通用类和配置文件)
content-server(主要的业务模块,游戏、加速相关业务逻辑)
log-server(日志分析)
模拟业务场景:用户使用加速器加速,加速服务器(VPN服务器)上报用户加速请求,并且上报自身的一些状态信息(单纯为了验证 Seata 接入是否正常,不要计较场景是否合理)。
步骤1:本地启动Seata,并初始化相关表结构
- 下载windows Seata 安装包,默认使用本地文件存储元数据和配置,直接启动即可
- 每个数据库都需要初始化一张undo 表(比如在这次初体验中,用到了两个数据库:content 和 log,需要在每个数据库中都创建一张undo 表),表结构从官网下载即可,我使用的是postgresql,需要稍微修改下表结构:
CREATE TABLE "public"."undo_log" (
"id" SERIAL NOT NULL primary key,
"branch_id" int8 NOT NULL,
"xid" varchar(100) COLLATE "default" NOT NULL,
"context" varchar(128) COLLATE "default" NOT NULL,
"rollback_info" bytea NOT NULL,
"log_status" int4 NOT NULL,
"log_created" timestamp(6) NOT NULL,
"log_modified" timestamp(6) NOT NULL,
"ext" varchar(100) COLLATE "default" DEFAULT NULL::character varying,
CONSTRAINT "unq_idx_ul_branchId_xid" UNIQUE ("branch_id", "xid")
)
WITH (OIDS=FALSE)
;
ALTER TABLE "public"."undo_log" OWNER TO "postgres";
步骤2:Spring 整合 Seata,使用XML 配置文件方式
-
base 项目引入Seata 所需配置文件,这两个配置文件从Seata 官方Demo 中获取即可,地址:seata demo
右侧红框中是必须的,主要是让Seata 能扫描到哪些方法需要使用分布式事务,可以类比Spring-tx 是如何扫描@Transaction 注解的。
以上三个截图,总结来说,就是不管你DAO 层使用什么样的数据源,都需要让Seata 提供的数据源代理类来包装一下,然后以前注入的数据源都替换成Seata 提供的代理数据源。
简单来说是因为Seata 的代理数据源对原本的数据源做了增强,在执行原本的SQL 语句的事务中,一并插入Seata 提供的Undo log。
步骤3:测试分布式事务
- 相关代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationContext-*.xml", "classpath:dispatcher-servlet.xml"})
public class SeataTest {
@Autowired
private SeataSupport seataSupport;
@Test
public void testSeataTx() {
seataSupport.setaTx();
}
}
====================================================================================
import com.alibaba.dubbo.config.annotation.Reference;
import com.hanhua.base.api.content.BoosterServerOnlineService;
import com.hanhua.base.api.log.BoosterConnectLogService;
import com.hanhua.base.api.user.UserDayFreeSpeedTaskService;
import com.hanhua.base.model.content.BoosterServerOnline;
import com.hanhua.base.model.log.BoosterConnectLog;
import com.hanhua.base.model.user.UserDayFreeSpeedTask;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Component;
import java.sql.Timestamp;
import java.util.Date;
@Component
public class SeataSupport {
@Reference
private BoosterServerOnlineService boosterServerOnlineService;
@Reference
private BoosterConnectLogService boosterConnectLogService;
@Reference
private UserDayFreeSpeedTaskService userDayFreeSpeedTaskService;
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
public void setaTx() {
BoosterConnectLog connectLog = new BoosterConnectLog();
connectLog.setUserId(123);
connectLog.setServerId(9999);
connectLog.setGameId(9999);
connectLog.setStatus(1);
connectLog.setCreateTime(new Date());
boosterConnectLogService.save(connectLog);
// UserDayFreeSpeedTask task = new UserDayFreeSpeedTask();
// task.setUserId(9999L);
// task.setDayFreeSpeedTaskId(1);
// task.setCreateTime(new Timestamp(System.currentTimeMillis()));
// userDayFreeSpeedTaskService.insert(task);
# 数据库ID 9999 已经存在,这里插入会报错主存
BoosterServerOnline online = new BoosterServerOnline();
online.setId(9999);
online.setWorking(1);
online.setPersonCount(10);
online.setContectedCount(online.getPersonCount());
boosterServerOnlineService.insert(online);
}
}
在该实验中,BoosterServerOnline 所在表的id 为9999 的记录已经存在,因此BoosterConnectLog 这条记录会被回滚。
遇到的坑(有遇到就记录)
1、Seata 使用fastjson 序列化,如果数据库字段是Timestamp类型,由于fastjson 序列化是导致精度丢失,在回滚的时候,会回滚失败
解决:不使用fastjson,使用默认的jackson 即可,因为jackson 考虑到了Timestamp 的精度问题