springboot整合seata_微服务OpenCloud中关于Seata处理分布式事务的建议

微服务OpenCloud中关于Seata处理分布式事务的建议

一、问题背景

现实微服务架构中,各个模块都是分数据库存储的。互相隔离,多数据源。而Open Cloud是基于Spring Cloud项目应运而生的,使用了阿里巴巴的Nacos作为微服务注册中心,随之发展而来的Spring Cloud Alibaba等产业链。

由于微服务各订单、各服务之间数据会出现一致性问题,事务不同步。传统的Spring AOP事务处理方式不能解决跨库跨项目引起的问题。为了解决这个问题,阿里先是推出了Rocket MQ消息中间件,带有解决分布式事务的方案。这几年,Seata是推出的另一款阿里的分布式事务解决方案,本文件谈谈Seata的主要功能和特点,并结合Open Cloud微服务Nacos注册中心来说明问题。

63b31f5d38cd571af6782bb22f991b90.png

二、Seata

Seata作为分布式事务的整体解决方案,在解决方案中引用了3种角色:

事务协调器:用来维护事物的运行状态,负责协调并驱动全局事务的提交或回滚。

控制全局事务的边界:开启全局事务,通过发起最终全局提交或全局回滚的决议。

控制分支事务:用来作为分支注册、状态汇报,接收事务协调器的指令,驱动本地事务的提交或回滚。

其工作模式如下:

41ac28d3bf9c6c6049c2c30be6aa8289.png

2.1Seata执行引擎

接下来介绍的是Seata的执行引擎:

(1)由TM推向TC截取开启一个GLobal Transaction,全局事务创建成功并生成一个全局唯一的XID.

(2)XID在微服务的链路调用中进行全局传播.

(3)RM通过处理分支事务,先解析这条SQL语句,Seata通过生成对应的UNDO_LOG记录来处理。

2.2Seata支持的微服务注册中心

Seata支持Zookeeper、Dubbo、Nacos、Apolo等注册中心,易用性广泛。

b67da66c3ca560140409a7b1fd573257.png

三、Open Cloud

Open Cloud作为一款开源微服务分布式框架架构,采用了Nacos作为微服务的注册中心,采用Spring Cloud Gateway网关作为动态路由,结合了Bus消息总线、定时任务中心、基于OAuth2+Spring Cloud Security的权限认证机制,很适合大众使用。

Open Cloud注定会流行起来,作为微服务架构的实践先驱。

4fcfa0fbb9f9133d064470c24299e659.png

四、本文采用的Seata解决方式

本文使用Seata的AT事务模式进行讲解。

AT模式有以下几个特点:

(1)支持本地的ACID机制,整体是通过2PC的传播。

(2)读写分离

4.1项目整合

本文采用的是Spring Boot 2.1.6+OpenCloud,注册中心是Nacos

引入Seata修改registry.conf

69668ffa652fa1cb256b943fc20079c6.png

使用file.conf,选择db模式,更改数据库属性连接。

transport {

# tcp udt unix-domain-socket

type = "TCP"

#NIO NATIVE

server = "NIO"

#enable heartbeat

heartbeat = true

# the client batch send request enable

enableClientBatchSendRequest = false

#thread factory for netty

threadFactory {

bossThreadPrefix = "NettyBoss"

workerThreadPrefix = "NettyServerNIOWorker"

serverExecutorThreadPrefix = "NettyServerBizHandler"

shareBossWorker = false

clientSelectorThreadPrefix = "NettyClientSelector"

clientSelectorThreadSize = 1

clientWorkerThreadPrefix = "NettyClientWorkerThread"

# netty boss thread size,will not be used for UDT

bossThreadSize = 1

#auto default pin or 8

workerThreadSize = "default"

}

shutdown {

# when destroy server, wait seconds

wait = 3

}

serialization = "seata"

compressor = "none"

}

## transaction log store, only used in server side

## global_table、branch_table和lock_table是需要建立的表,存储会话和锁信息,可以给server使用单独的数据库。

store {

## store mode: file、db

mode = "db"

db {

## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.

datasource = "druid"

## mysql/oracle/h2/oceanbase etc.

dbType = "mysql"

driverClassName = "com.mysql.jdbc.Driver"

url = "jdbc:mysql://localhost:3306/seata"

user = "root"

password = "root"

minConn = 1

maxConn = 10

globalTable = "global_table"

branchTable = "branch_table"

lockTable = "lock_table"

queryLimit = 100

}

}

## server configuration, only used in server side

server {

recovery {

#schedule committing retry period in milliseconds

committingRetryPeriod = 1000

#schedule asyn committing retry period in milliseconds

asynCommittingRetryPeriod = 1000

#schedule rollbacking retry period in milliseconds

rollbackingRetryPeriod = 1000

#schedule timeout retry period in milliseconds

timeoutRetryPeriod = 1000

}

undo {

logSaveDays = 7

#schedule delete expired undo_log in milliseconds

logDeletePeriod = 86400000

}

#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent

maxCommitRetryTimeout = "-1"

maxRollbackRetryTimeout = "-1"

rollbackRetryTimeoutUnlockEnable = false

}

## metrics configuration, only used in server side

metrics {

enabled = false

registryType = "compact"

# multi exporters use comma divided

exporterList = "prometheus"

exporterPrometheusPort = 9898

}

创建以下几个表,建立业务场景:

CREATE TABLE `branch_table` (

`branch_id` bigint(20) NOT NULL,

`xid` varchar(128) NOT NULL,

`transaction_id` bigint(20) DEFAULT NULL,

`resource_group_id` varchar(32) DEFAULT NULL,

`resource_id` varchar(256) DEFAULT NULL,

`lock_key` varchar(128) DEFAULT NULL,

`branch_type` varchar(8) DEFAULT NULL,

`status` tinyint(4) DEFAULT NULL,

`client_id` varchar(64) DEFAULT NULL,

`application_data` varchar(2000) DEFAULT NULL,

`gmt_create` datetime DEFAULT NULL,

`gmt_modified` datetime DEFAULT NULL,

PRIMARY KEY (`branch_id`),

KEY `idx_xid` (`xid`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `lock_table` (

`row_key` varchar(128) NOT NULL,

`xid` varchar(96) DEFAULT NULL,

`transaction_id` mediumtext,

`branch_id` mediumtext,

`resource_id` varchar(256) DEFAULT NULL,

`table_name` varchar(32) DEFAULT NULL,

`pk` varchar(32) DEFAULT NULL,

`gmt_create` datetime DEFAULT NULL,

`gmt_modified` datetime DEFAULT NULL,

PRIMARY KEY (`row_key`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

紧接着启动seata

4.2项目引入Seata坐标

引入Seata与Spring Boot项目的坐标依赖:

io.seata

seata-spring-boot-starter

1.2.0

io.seata

seata-all

io.seata

seata-all

1.2.0

至此,项目中就引入了Seata的坐标工具类

4.3配置yml

在application-prod.yml中引入Seata开启配置信息

seata:

enabled: true

application-id: ${spring.application.name}

tx-service-group: xxdi_tx_group

4.4配置数据源代理扫描器

配置数据源全局代理扫描工作类提供Seata全局事务扫描

@Configuration

public class OpenCloudSeataConfig {

@Value("${spring.application.name}")

private String applicationId;

// 第二个参数是file.conf中service.vgroupMapping.后面的值

@Bean

public GlobalTransactionScanner globalTransactionScanner() {

return new GlobalTransactionScanner(applicationId, "xxdi_tx_group");

}

@Bean

@ConfigurationProperties(prefix = "spring.datasource.hikari")

public DataSource dataSource(DataSourceProperties properties) {

HikariDataSource hikariDataSource =

properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();

return new DataSourceProxy(hikariDataSource);

}

4.5开启分布式事务

在需要开启分布式事务的代码上加上@GlobalTransactional注解

就开启了分布式事务,在DutyService中的示例,开启Feign调用,RoleService是另外一个微服务工程中的Feign服务

例如:

@GlobalTransactional(name="oc-test",rollbackFor=BaseException.class)

public Duty saveDuty(Duty duty)

{

// 输入本地事务

DutyDao dutyDao = new DutyDao();

dutyDao.insert(duty);

//Feign事务

Duty duty2 = roleService.sku("7799005533");

return duty2;

}

name取一事务名称,rollback指定回滚Exception

4.6开启Feign拦截器

在Feign调用过程中加入XID处理记录状态

@Component

public class FeignInterceptor implements RequestInterceptor {

@Override

public void apply(RequestTemplate template) {

String xid = RootContext.getXID();

if (StringUtils.isNotBlank(xid)) {

template.header("xid", xid);

}

}

}

4.7各个子系统引入undo_log

子系统需要引入undo_log日志记录表,记录回滚状态日志信息:

CREATE TABLE `undo_log` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`branch_id` bigint(20) NOT NULL,

`xid` varchar(100) NOT NULL,

`context` varchar(128) NOT NULL,

`rollback_info` longblob NOT NULL,

`log_status` int(11) NOT NULL,

`log_created` datetime NOT NULL,

`log_modified` datetime NOT NULL,

`ext` varchar(100) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

af95a63e38c6058a3a331ec92e702110.png

五、总结

Seata为分布式事务提供了解决方案,适合于大型分布式系统,所有用户使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值