官网:https://seata.io/zh-cn/docs/overview/what-is-seata.html
源码:https://github.com/seata/seata
seata 是一款开源的分布式事务解决方案,致力于高性能和简单易用的分布式事务服务。Seatea 将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云有商用版本的GTS(Global Transaction Service 全局事务服务)
事务(Transaction)是访问并可能更型数据库中各个数据项的一个执行单元(unit)。在关系型数据库中,一个事务由一组SQL语句组成。事务应该具有4个属性:
其中,TC为单独部署的Server服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
(1) TM 请求 TC 开启一个全局事务,TC 会生成一个 XID 作为该全局事务的编号。XID 会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。(全局事务信息存储在 global_table )
(2) RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
(3) TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。(事务参与者存储在branch_table)
(4) TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。
版本选择查看第一篇文章
官方文档:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html
步骤一:下载启动包
步骤二:建表
对应sql文件脚本地址:https://github.com/seata/seata/tree/1.4.2/script/server/db
进成后如下所示:
global_table:全局事务信息
branch_table :事务参与者信息(分支事务)
lock_table:全局锁
步骤三:修改存储模式为db
启动包: seata–>conf–>file.conf,修改store.mode=“db” ;并且修改db数据库相关配置(如url、user、password之类的)
源码: 根目录–>seata-server–>resources–>file.conf,修改store.mode=“db”
步骤四:修改注册中心
启动包: seata–>conf–>registry.conf,修改type = “nacos”;并填写nacos相关配置
源码: 根目录–>seata-server–>resources–>registry.conf,修改type = “nacos”
步骤五:修改配置中心
启动包: seata–>conf–>registry.conf,修改config.type = “nacos”;并填写nacos相关配置
步骤六:将配置注册到nacos \script\config-center\nacos(此处需要使用到上面下载的资源)
完整配置信息文件地址:https://github.com/seata/seata/blob/1.4.2/script/config-center/config.txt
注意坑:使用mysql 8 数据库请注意:
先检查 lib目录下面是否有mysql 8依赖(如果seata是1.4.2,mysql依赖在lib/jdbc
下),如果没有需要添加“mysql-connector-java-8.0.19.jar”
注意驱动类:store.db.driverClassName = com.mysql.cj.jdbc.Driver
(不是com.mysql.jdbc.Driver)
注意mysql url 需要带上时区信息:
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
-h: 注册到注册中心的ip
-p: Server rpc 监听端口
-g: 配置分组,默认值为 ‘SEATA_GROUP’
-m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis)
-n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
-e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
-t: 租户信息,对应Nacos的命名空间ID字段,默认值为空
-h: 注册到注册中心的ip
-p: Server rpc 监听端口
-m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3及以上版本支持redis),mo
-n: Server node,多个Server时,需区分各自节点,用于生成不同区间的transactionId,以免冲突
-e: 多环境配置参考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
ERROR --- [ionPool-Create-1033638837] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true, errorCode 0, state 08001
==>
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_282]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_282]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_282]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_282]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:389) ~[na:na]
at com.mysql.jdbc.Util.getInstance(Util.java:372) ~[na:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:958) ~[na:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:937) ~[na:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926) ~[na:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:872) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2316) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2069) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.(ConnectionImpl.java:794) ~[na:na]
at com.mysql.jdbc.JDBC4Connection.(JDBC4Connection.java:44) ~[na:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_282]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_282]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_282]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_282]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:389) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:399) ~[na:na]
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:325) ~[na:na]
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1644) ~[druid-1.1.23.jar:1.1.23]
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1710) ~[druid-1.1.23.jar:1.1.23]
at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2774) ~[druid-1.1.23.jar:1.1.23]
Caused by: java.lang.NullPointerException: null
at com.mysql.jdbc.ConnectionImpl.getServerCharset(ConnectionImpl.java:2989) ~[na:na]
at com.mysql.jdbc.MysqlIO.sendConnectionAttributes(MysqlIO.java:1873) ~[na:na]
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1802) ~[na:na]
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1206) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2239) ~[na:na]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2270) ~[na:na]
... 13 common frames omitted
解决方案: 先排查mysql url等配置信息是否错误,然后查看mysql版本。本人是MySQL版本问题,用的是MySQL8.0,Seata store.db.url
配置没有加上时区信息,其次还有修改MySQL驱动类(1.4.2对应的配置是:store.db.driverClassName)
seata 其他版本我不太清楚,反正1.4.2是有提供MySQL的jar依赖,在lib/jdbc目录下
添加两张业务表(一个订单表、一个库存表)
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
dependency>
# seata 配置
seata:
tx-service-group: my_test_tx_group # 对应nacos配置 service.vgroupMapping.my_test_tx_group
service:
vgroup-mapping:
my_test_tx_group: 'default' # 对应nacos配置 service.vgroupMapping.my_test_tx_group 的值 default
registry:
type: nacos
nacos: # 对应配置类:RegistryNacosProperties
server-addr: ${spring.cloud.nacos.discovery.server-addr}
application: seata-server # seata server 的服务名,默认:seata-server
username: ${spring.cloud.nacos.discovery.username}
password: ${spring.cloud.nacos.discovery.password}
group: SEATA_GROUP # seata server所在的组名,默认为:SEATA_GROUP
namespace: ${spring.cloud.nacos.discovery.namespace}
config:
type: nacos
server-addr: ${spring.cloud.nacos.discovery.server-addr}
username: ${spring.cloud.nacos.discovery.username}
password: ${spring.cloud.nacos.discovery.password}
group: SEATA_GROUP
namespace: ${spring.cloud.nacos.discovery.namespace}
注意事项:
如果没有注意上方两点将会导致启动时报:no available service ‘default’ found, please make sure registry config correct。
SEATA AT 模式需要 UNDO_LOG 表(使用到的数据库都需要此表)
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT ,
`branch_id` bigint(20) NOT NULL COMMENT '分支事务ID',
`xid` varchar(100) NOT NULL COMMENT '全局事务ID',
`context` varchar(128) NOT NULL COMMENT '上下文',
`rollback_info` longblob NOT NULL COMMENT '回滚信息',
`log_status` int(11) NOT NULL COMMENT '状态,0正常,1全局已完成',
`log_created` datetime NOT NULL COMMENT '创建时间',
`log_modified` datetime NOT NULL COMMENT '修改时间',
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT = 'AT transaction mode undo table';
UNDO_LOG 表名是可以配置的,对应的文档地址
可在此处修改表名:
将@Transactional改为@GlobalTransactional
文章一 、文章二、文章三