建站平台步骤详解,甘孜州手机网站建设,wordpress数据库安装失败,青岛网站设计方案文章目录 前言一、pom jar导入:二、项目配置#xff1a;2.1 配置 说明#xff1a;2.1 .1 seata server 端:2.1 .2 seata client 端: 2.2 开启seata 对于数据源的代理:2.3 seata-client 的注册中心#xff1a;2.4 seata-client 的配置中心#xff1a;2.5 去掉手写的数据源代… 文章目录 前言一、pom jar导入:二、项目配置2.1 配置 说明2.1 .1 seata server 端:2.1 .2 seata client 端: 2.2 开启seata 对于数据源的代理:2.3 seata-client 的注册中心2.4 seata-client 的配置中心2.5 去掉手写的数据源代理和feign代理 三、项目使用3.1 AT 模式使用3.2 XA 模式使用3.3 TCC 模式使用 四、总结五、参考 前言
在了解了Seata 的工作原理后如何在springboot 项目中进行整合使用本文进行阐述。 提示本文使用 seata 版本 1.5 ;在整合之前 你需要先要部署seate 的服务
一、pom jar导入:
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.7.14/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIdspring-seata-storage/artifactIdversion0.0.1-SNAPSHOT/versionnamespring-seata-storage/namedescriptionspring-seata-storage/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactIdoptionaltrue/optionalversion3.1.6/version/dependency!-- mysql --!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.2/version/dependency!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.21/version!-- version5.1.49/version--/dependency!-- https://mvnrepository.com/artifact/net.sf.jsqlparser/jsqlparser --dependencygroupIdnet.sf.jsqlparser/groupIdartifactIdjsqlparser/artifactIdversion0.8.0/version/dependencydependencygroupIdio.swagger/groupIdartifactIdswagger-annotations/artifactIdversion1.6.2/version/dependency!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter --!-- dependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.5.2/version/dependency--!--处理 在SpringBoot 2.4.x的版本之后对于bootstrap.properties/bootstrap.yaml配置文件(我们合起来成为Bootstrap配置文件)的支持需要导入如下的依赖--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-bootstrap/artifactIdversion3.1.6/version/dependency!-- seata 使用nacos 作为配置中心和 注册中心时 使用 --dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactIdversion2021.0.5.0/version/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactIdversion2021.0.5.0/version/dependencydependencygroupIdcom.alibaba.cloud/groupId!-- 里面已经集成服务间调用X-id的传递包括FeignClient的重写如果在之前自定义封装过Feign注意启动冲突--artifactIdspring-cloud-starter-alibaba-seata/artifactIdversion2021.0.5.0/versionexclusions!--去除低版本--exclusiongroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactId/exclusion/exclusions/dependency!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter --dependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.6.1/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationmainClasscom.example.springseata.springseatastorage/mainClass/configurationexecutionsexecutiongoalsgoalrepackage/goal/goals/execution/executions/plugin/pluginsresourcesresourcedirectorysrc/main/resources/directoryfilteringtrue/filtering/resource/resources/build/project
jar 目的
spring-boot-starter-web 使用 浏览器进行 http 访问lombok 生成 get set 等方法使代码更加简洁spring-boot-starter-test 可以进行单元测试spring-cloud-starter-openfeign 可以使用feign 接口进行服务间的http 通信mybatis-plus-boot-startemysql-connector-java jsqlparser导入mysql-plus 进行 增删改查swagger-annotations 类和方法的说明spring-cloud-starter-bootstrap 在SpringBoot 2.4.x的版本之后对于bootstrap.properties/bootstrap.yaml配置文件(我们合起来成为Bootstrap配置文件)的支持spring-cloud-starter-alibaba-nacos seata 使用nacos 作为配置中心和 注册中心时 使用spring-cloud-starter-alibaba-seata 导入cloud对饮的jar 让其帮助进行 分布式事务xid 的传递seata-spring-boot-starter seata 的核销服务jar
springcloud springbootspringcloud-alibaba 版本对应版本说明
二、项目配置
2.1 配置 说明
目的 了解seat 客户端和服务端的 关系清楚其配置的含义
2.1 .1 seata server 端: seata server 的存储模式 存储模式指定了Seata Server用于存储事务和元数据的方式这个只有在seata 服务端才有。Seata Server支持三种存储模式file、db和Redis。列如 当开始事务时需要生成全局事务id Xid 在生成后就需要Seata 服务进行存储所以他是Seata Server的重要组成部分 seata server 的配置中心 配置中心的作用用于配置 Seata Server的相关参数 Seata Server 端在启动的时候也可以配置很多参数如果把这些参数都放到 Seata Server 中每次修改度需要连接到Seata Server 的服务器然后对其配置文件进行修改如果把这个参数放到 配置中心如config zknacos 就可以在配置中心进行修改更加的方便 seata server 的注册中心 Seata Server 的注册中心是用于存储和管理 Seata 事务参与者的信息的组件多个Seata Server 可以成为一个集群然后注册到zkeurekanacos 里这样方便集群的管理
2.1 .2 seata client 端: seata client 的配置中心 同seata-sever 端相同客户端在启动的时候也可以进行参数的配置所以可以在客户端我们也可以 将参数放到 config zknacos 进行配置 seata client 的注册中心 seata 客户端的注册中心用于服务端的发现一个重要的信息就是可以将seata 客户端和seata 服务端 注册到相同的地方已nacos 为例服务端和客户端 注册到相同命名空间相同分组相同应用此时客户端会动态的发现 seata 服务的地址此时不需要在显示的设置 seata 服务断的地址 Seata 客户端在启动时会从 Nacos 服务获取 service.vgroupMapping.your-service-group 定义的服务群组实例这需要和 Seata 服务端的配置保持一致。以下三个参数需要和 Seata 服务端保持一致application:, group, namespace
2.2 开启seata 对于数据源的代理:
目的让 seata 接管数据源然后才能协调各个微服务的本地事务
seata:enabled: true # spring 自动装配enable-auto-data-source-proxy: true # 开启数据源自动代理transport:enable-client-batch-send-request: true #客户端事务消息请求是否批量合并发送,默认truefalse单条发送tx-service-group: my_tx_group # 事务分组application-id: storageservice:vgroup-mapping:my_tx_group: default
# grouplist:
# default: localhost:8091tx-service-group 定义事务分组可以自定义多个微服务只有在同一个事务分组中分布式事务才能生效也就是多个微服务改参数要保持一致application-id 该服务事务id 同一分组下的 的微服务 需要进行不同的定义servicevgroup-mapping 定义改事务分组 连接的seata 服务端 集群的名称改名称需要和seata server 定义的集群名称保持一致 grouplist 中 default 为集群的名称后面的为集群seata-server 端的地址注意 这中连接模式只在 seata 客户端为 file 模式下生效 其他方式如nacos 需要配置使得 seata-server 和seata-client 都注册在相同命名空间相同分组相同application 下这样seata-client 就可以动态的发现seata-server 的实例地址
2.3 seata-client 的注册中心
目的 了解本地springboot 服务如何连接到 seata server 端
客户端和服务端 seata 都是默认已file 作为注册中心 file seata-server 服务端 注册中心使用的file 本地模式则seata 客户端 也需要使用file注册模式 此时需要配置 seata.service.grouplist.default127.0.0.1:8091 用于客户端连接 seata 服务端 nacos seata-server 服务端 注册中心使用的nacos则seata 客户端 也需要使用nacos注册模式 seata-server 服务端 注册中心使用的nacos则seata 客户端 也需要使用nacos注册模式
seata:enabled: true # spring 自动装配enable-auto-data-source-proxy: true # 开启数据源自动代理transport:enable-client-batch-send-request: true #客户端事务消息请求是否批量合并发送,默认truefalse单条发送tx-service-group: my_tx_groupapplication-id: storageservice:vgroup-mapping:my_tx_group: default
# grouplist:
# default: localhost:8091registry:# type: filetype: nacosnacos:application: seata-serverserver-addr: localhost:8848group: SEATA_GROUPnamespace: 7f2765bc-d8c1-4b49-b706-8c292d2ffac9username: nacospassword: nacos注意 applicationserver-addrgroupnamespaceusernamepassword 客户端和服务端报纸一致
2.4 seata-client 的配置中心
目的 了解本地springboot 如何对seata 进行参数配置
客户端和服务端 seata 都是默认已file 作为配置中心他们的只是用作参数的配置使用所以他们各自是独立的
file seata 客户端的默认配置nacos 使用nacos 作为配置中心 :
seata:enabled: true # spring 自动装配enable-auto-data-source-proxy: true # 开启数据源自动代理transport:enable-client-batch-send-request: true #客户端事务消息请求是否批量合并发送,默认truefalse单条发送tx-service-group: my_tx_groupapplication-id: storageservice:vgroup-mapping:my_tx_group: default
# grouplist:
# default: localhost:8091config:type: nacos
# type: filenacos:server-addr: localhost:8848namespace: c0befc56-f31b-4de6-929b-6f75604992d1
# namespace: 7f2765bc-d8c1-4b49-b706-8c292d2ffac9group: DEFAULT_GROUPusername: nacospassword: nacos##if use MSE Nacos with auth, mutex with username/password attribute#access-key: #secret-key: data-id: seata.properties本文测试时虽然在 nacos 中创建了seata.properties 的配置文件但是其 配置的参数没有起作用; 会提示无法找到service.vgroupMapping.my_tx_groupmy_tx_group 为定义的分组名称的集群通过创建 service.vgroupMapping.my_tx_group 配置文件 并且定义default 得以解决目前没有找到其它解决办法
2.5 去掉手写的数据源代理和feign代理
seata DataSourceProxy 的代理因为在其配置文件中已经开启了自动代理 seata:enabled: true # spring 自动装配enable-auto-data-source-proxy: true # 开启数据源自动代理
所以需要去重 手动加入的代理防止冲突导致 事务失效feign 接口的代理 因为引入的spring-cloud-starter-alibaba-seata 已经对feign 进行了xid 的处理
dependencygroupIdcom.alibaba.cloud/groupId!-- 里面已经集成服务间调用X-id的传递包括FeignClient的重写如果在之前自定义封装过Feign注意启动冲突--artifactIdspring-cloud-starter-alibaba-seata/artifactIdversion2021.0.5.0/versionexclusions!--去除低版本--exclusiongroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactId/exclusion/exclusions
/dependency所以可以去除 手动增加的对于feign 接口xid 的特殊处理防止冲突
三、项目使用
3.1 AT 模式使用
seata 客户端的事务模式默认使用AT 模式通过以下两个步骤配置就可使用
在每个 数据库中增加undo_log 表用于AT 模式事务日志记录使用
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)
) ENGINEInnoDB AUTO_INCREMENT8 DEFAULT CHARSETutf8;在不同服务中都完成seata 的集群 然后在对应的业务实现中增加GlobalTransactional(rollbackFor Exception.class) 注解
3.2 XA 模式使用
mysql 开启XA协议
SHOW VARIABLES LIKE innodb_support_xa;如果没有开通则打开 MySQL 配置文件 my.cnf 或 my.ini。在文件中找到 [mysqld] 部分并在该部分下添加以下行
[mysqld]
innodb_support_xa 1保存配置文件并重启 MySQL 服务
显示设置 模式为XA 模式代码层面无需变动 seata.data-source-proxy-modeXA3.3 TCC 模式使用
根据两阶段行为模式的不同我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 TCC (Branch) Transaction Mode.
AT 模式参考链接 TBD基于 支持本地 ACID 事务 的 关系型数据库
一阶段 prepare 行为在本地事务中一并提交业务数据更新和相应回滚日志记录。 二阶段 commit 行为马上成功结束自动 异步批量清理回滚日志。 二阶段 rollback 行为通过回滚日志自动 生成补偿操作完成数据回滚。 相应的TCC 模式不依赖于底层数据资源的事务支持
一阶段 prepare 行为调用 自定义 的 prepare 逻辑。 二阶段 commit 行为调用 自定义 的 commit 逻辑。 二阶段 rollback 行为调用 自定义 的 rollback 逻辑。 所谓 TCC 模式是指支持把 自定义 的分支事务纳入到全局事务的管理中。
tcc 事务记录表tcc_fence_log
CREATE TABLE tcc_fence_log (xid varchar(128) NOT NULL COMMENT global id,branch_id bigint(20) NOT NULL COMMENT branch id,action_name varchar(64) NOT NULL COMMENT action name,status tinyint(4) NOT NULL COMMENT status(tried:1;committed:2;rollbacked:3;suspended:4),gmt_create datetime(3) NOT NULL COMMENT create time,gmt_modified datetime(3) NOT NULL COMMENT update time,PRIMARY KEY (xid,branch_id),KEY idx_gmt_modified (gmt_modified),KEY idx_status (status)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4;订单服务 java demo IOrderService
public interface IOrderService extends IServiceOrder {Boolean orderCreate(OrderReqDto reqDto);
}
实现类
Override
GlobalTransactional(rollbackFor Exception.class)public Boolean orderCreate(OrderReqDto reqDto) {log.error(Seata全局事务id{}, RootContext.getXID());// stock服务未报错order服务报错try {// 扣减库存-通过fegin 接口进行调用storageFeignApi.tccSumGoodNum(reqDto.getByGoods());} catch (Exception e) {e.printStackTrace();throw new RuntimeException(e);}// 订单创建Order order new Order();order.setAccountId(123).setMoney(reqDto.getOrderMoney());this.save(order);return true;}
controller
ResponseBodyPostMapping(/tcc/sure)public Boolean orderCreate(RequestBody OrderReqDto reqDto) throws SQLException {return orderService.orderCreate(reqDto);}库存服务 java demo controller ResponseBodyPostMapping(value /tcc/sumGoodNum)public boolean tccSumGoodNum(RequestBody ListGoodDto param){return tccStorageService.deduct(param);}ITccStorageService
package com.example.springseatastorage.service;import com.example.springseatastorage.controller.dto.GoodDto;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;import java.util.List;LocalTCC
public interface ITccStorageService {/*** 减库存* useTCCFencetrue 为开启防悬挂* param param*/TwoPhaseBusinessAction(name deduct, commitMethod busCommit, rollbackMethod busRollback, useTCCFence true)boolean deduct(BusinessActionContextParameter(paramName param) ListGoodDtoparam);/*** 提交事务** param actionContext* return*/boolean busCommit(BusinessActionContext actionContext);/*** 回滚事务** param actionContext* return*/boolean busRollback(BusinessActionContext actionContext);
}
ITccStorageServicempl
package com.example.springseatastorage.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.springseatastorage.controller.dto.GoodDto;
import com.example.springseatastorage.entity.Storage;
import com.example.springseatastorage.service.IStorageService;
import com.example.springseatastorage.service.ITccStorageService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;Slf4j
Service
public class ITccStorageServicempl implements ITccStorageService {Autowiredprivate IStorageService storageService;OverrideTransactional(rollbackFor Exception.class, propagation Propagation.REQUIRED)public boolean deduct(ListGoodDto param) {log.error(Seata全局事务id{}, RootContext.getXID());MapString, Integer good param.stream().collect(Collectors.toMap(GoodDto::getGoodId, GoodDto::getBuyNum));LambdaQueryWrapperStorage wrapper new LambdaQueryWrapper();wrapper.in(Storage::getGoodId, param.stream().map(e - e.getGoodId()).collect(Collectors.toList()));ListStorage storages storageService.list(wrapper);storages.stream().forEach(e - {e.setGoodNum(e.getGoodNum() - good.get(e.getGoodId()));});storageService.updateBatchById(storages);return true;}Overridepublic boolean busCommit(BusinessActionContext actionContext) {log.info(busCommit xid actionContext.getXid() 提交成功);return true;}Overridepublic boolean busRollback(BusinessActionContext actionContext) {// 编写对应的业务数据进行回滚ListGoodDto param actionContext.getActionContext(param, List.class);
// int count actionContext.getActionContext(count, Integer.class);
// LambdaQueryChainWrapperStock eq lambdaQuery().eq(Stock::getCommodityCode, commodityCode);
// Long count1 eq.one().getCount();
// // 扣了多少数需要重新添加回去
// lambdaUpdate().set(Stock::getCount, count count1)
// .eq(Stock::getCommodityCode, commodityCode)
// .update();log.info(busRollback xid actionContext.getXid() 回滚成功);return true;}
} 四、总结
本文通过springboot 项目整合seata 然后 通过 ATXATCC 事务模式模拟实现分布式事务。
五、参考
1 Seata新手部署指南 2 版本说明 3 Seata 事务模式