首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,146 阅读
2
Mac打印机设置黑白打印
4,517 阅读
3
修改elementUI中el-table树形结构图标
4,516 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,351 阅读
5
intelliJ Idea 2022.2.X破解
4,060 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
570
篇文章
累计收到
1,424
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
2
篇与
的结果
2021-07-26
Seata基础教程下-Spring Cloud Alibaba使用Seata
在seata基础教程上-seata 1.4.2的安装及基于Nacos的配置中,我们基于Nacos完成了Seata的安装及配置,这篇文章我们介绍下在Spring Cloud Alibaba中的使用。环境准备学习本文之前,你应该准备好以下环境。Nacos 2.0.2版本并运行Seata Server 1.4.2版本,配置好Nacos并运行前置知识本文通过Spring Cloud Alibaba,使用Nacos作为服务中心,通过OpenFeign实现服务之间的调用,所以你必须具备以下基础知识:在Spring Cloud中使用Nacos作为服务注册、发现中心的配置及使用。在Spring Cloud中通过OpenFeign实现服务之间的调用。MyBatis的使用。场景描述本文参考尚硅谷阳哥的视频教程,使用最新的Nacos(2.0.2)及Seata(1.4.2),演示用户下单的业务场景,基本流程如下其实业务很简单,用户下单,扣减商品库存表数据然后减去账户余额,如果不是微服务架构,我们一般使用一个系统,基本上一个@Transactional注解就解决了。基于微服务就不同了,我们提供三个微服务,分别为订单服务、库存服务、账户服务,三个服务对应三个数据库,分别为seata_order、seata_storage、seata_account,现在不同业务分布在不同的数据库中,那么我们在进行数据回滚就不太好处理了。Seata的分布式交易解决方案有了Seata就简单了,我们只需要使用一个@GlobalTransactional注解在业务方法上就可以了。Spring Cloud使用Seata创建数据库创建三个数据库,分别为seata_order、seata_storage、seata_account。订单微服务表结构create table t_order ( id bigint auto_increment comment '主键', user_id bigint null comment '用户Id', product_id bigint null, count int null, money decimal(11) null, status int(1) null, constraint t_order_id_uindex unique (id) ); alter table t_order add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8;账户微服务表结构create table t_account ( id bigint auto_increment, user_id bigint null, total decimal null, used decimal null, residue decimal null, constraint t_account_id_uindex unique (id) ); alter table t_account add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_account.t_account (id, user_id, total, used, residue) VALUES (1, 1, 1000, 0, 1000);库存微服务表结构create table t_storage ( id bigint auto_increment, product_id bigint null, total int null, used int null, residue int null comment '剩余库存', constraint t_storage_id_uindex unique (id) ); alter table t_storage add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_storage.t_storage (id, product_id, total, used, residue) VALUES (2, 1, 100, 0, 100);创建工程工程结构,如下图,请忽略其他模块。[alt type="warning"]一定要注意依赖里面的版本号,不然会出现各种错误。[/alt]创建父工程父工程用于维护依赖管理,没有具体代码。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.xiangcaowuyu</groupId> <artifactId>Cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>cloud-provider-payment8001</module> <module>cloud-consumer-order80</module> <module>cloud-api-common</module> <module>cloud-provider-payment8004</module> <module>cloud-consumerzk-order80</module> <module>cloud-consumer-feign-order80</module> <module>cloud-provider-hystrix-payment8001</module> <module>cloud-consumer-feign-hystrix-order80</module> <module>cloud-consumer-hystrix-dashboard9001</module> <module>cloud-gateway-gateway9527</module> <module>cloud-config-center-3344</module> <module>cloud-config-client-3355</module> <module>cloud-config-client-3366</module> <module>cloud-stream-rabbitmq-provider8801</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloudalibaba-provider-payment9001</module> <module>cloudalibaba-provider-payment9002</module> <module>cloudalibaba-consumer-order80</module> <module>cloudalibaba-config-nacos-client3377</module> <module>cloudalibaba-sentinel-service8401</module> <module>seata-order-service2001</module> <module>seata-storage-service2002</module> <module>seata-account-service2003</module> </modules> <!-- 统一管理jar包版本--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>en,fr</locales> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> </plugin> </plugins> </reporting> </project> 创建通用模块通用模块这里维护了一个CommonResult用于返回Json对象。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>net.xiangcaowuyu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-api-common</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> </dependencies> </project>增加实体package net.xiangcaowuyu.springcloud.entites; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * 博客:https://www.xiangcaowuyu.net * Description: * * @Author: 香草物语 * DateTime: 2021-07-24 11:25 */ @Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { private Integer code; private String message; private T data; public CommonResult(Integer code, String message) { this(code, message, null); } }创建订单微服务模块订单模块维护订单微服务,通过Feign调用库存及账户微服务。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>net.xiangcaowuyu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-order-service2001</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>net.xiangcaowuyu</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件配置文件一定要注意,tx-service-group一定要与我们Seata服务端的配置保持一致。server: port: 2001 spring: application: name: seata-order-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Order { /** * 主键 */ private Long id; /** * 用户Id */ private Long userId; private Long productId; private Integer count; private BigDecimal money; private Integer status; }增加Mapperpublic interface OrderDao { int create(Order record); int update(@Param("userId") Long userId, @Param("status") Integer status); 数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.OrderDao"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Order"> <!--@Table t_order--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="count" jdbcType="INTEGER" property="count"/> <result column="money" jdbcType="DECIMAL" property="money"/> <result column="status" jdbcType="INTEGER" property="status"/> </resultMap> <sql id="Base_Column_List"> <!--@mbg.generated--> id, user_id, product_id, `count`, money, `status` </sql> <insert id="create" parameterType="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Order"> <!--@mbg.generated--> insert into t_order (user_id, product_id, `count`, money, `status`) values (#{userId,jdbcType=BIGINT}, #{productId,jdbcType=BIGINT}, #{count,jdbcType=INTEGER}, #{money,jdbcType=DECIMAL}, #{status,jdbcType=INTEGER}) </insert> <update id="update"> update t_order set status = 1 where user_id = #{userId} and status = #{status} </update> </mapper>增加服务接口增加账户微服务Feign调用接口@FeignClient(value = "seata-account-service") public interface IAccountService { @PostMapping("/account/decrease") public CommonResult accountDecrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money); }增加库存微服务Feign调用接口@FeignClient(value = "seata-storage-service") public interface IStorageService { @PostMapping("/storage/decrease") public CommonResult storageDecrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count); } 增加订单接口public interface IOrderService { int create(Order record); }增加订单服务实现@Service @Slf4j public class OrderServiceImpl implements IOrderService { @Resource private OrderDao orderDao; @Resource private IStorageService storageService; @Resource private IAccountService accountService; @Override @GlobalTransactional(name = "fsp-create-order") // @Transactional public int create(Order order) { log.info("--------->开始新建订单"); orderDao.create(order); log.info("--------->订单微服务开始调用库存,做扣减Count"); storageService.storageDecrease(order.getProductId(), order.getCount()); log.info("--------->订单微服务开始调用库存,做扣减End"); log.info("--------->订单微服务开始调用账户,做扣减Money"); accountService.accountDecrease(order.getUserId(), order.getMoney()); log.info("--------->订单微服务开始调用账户,做扣减End"); log.info("--------->修改订单状态开始"); orderDao.update(order.getUserId(), 0); log.info("--------->修改订单状态End"); return 1; } }增加接口controller@RestController @Slf4j @RequestMapping("/order") public class OrderController { @Resource private IOrderService orderService; @PostMapping("/create") public CommonResult create(Order order) { orderService.create(order); return new CommonResult(200, "订单创建成功"); } }增加DataSource,让Seata代理数据库@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"}) public class SeataOrderServiceMain2001 { public static void main(String[] args) { SpringApplication.run(SeataOrderServiceMain2001.class, args); } }创建库存微服务模块库存微服务用于提供扣减库存的微服务接口。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>net.xiangcaowuyu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-storage-service2002</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>net.xiangcaowuyu</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2002 spring: application: name: seata-storage-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_storage?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Storage { private Long id; private Long productId; private Integer total; private Integer used; private Integer residue; }增加Mapperpublic interface StorageDao { int decrease(@Param("productId") Long productId, @Param("count") Integer count); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.StorageDao"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Storage"> <!--@Table t_storage--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="total" jdbcType="INTEGER" property="total"/> <result column="used" jdbcType="INTEGER" property="used"/> <result column="residue" jdbcType="INTEGER" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, product_id, total, used, residue </sql> <update id="decrease"> update t_storage set used = used + #{count}, residue = residue - #{count} where product_id = #{productId} </update> </mapper>增加服务接口public interface IStorageService { int decrease(Long productId, Integer count); } 增加服务实现@Service @Slf4j public class StorageServiceImpl implements IStorageService { @Resource private StorageDao storageDao; @Override // @Transactional public int decrease(Long productId, Integer count) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); return storageDao.decrease(productId, count); } }增加接口controller@RestController @RequestMapping("/storage") @Slf4j public class StorageController { @Resource private IStorageService storageService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) { storageService.decrease(productId, count); return new CommonResult(200, "扣减库存成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"}) public class SeataStorageServiceMain2002 { public static void main(String[] args) { SpringApplication.run(SeataStorageServiceMain2002.class, args); } }创建账户微服务模块账户微服务用于提供扣减账户金额的微服务接口。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>net.xiangcaowuyu</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-account-service2003</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>net.xiangcaowuyu</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2003 spring: application: name: seata-account-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: net.xiangcaowuyu.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Account { private Long id; private Long userId; private Long total; private Long used; private Long residue; }增加Mapperpublic interface AccountDao { int decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.xiangcaowuyu.springcloud.alibaba.seata.dao.AccountDao"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.springcloud.alibaba.seata.domain.Account"> <!--@Table t_account--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="total" jdbcType="DECIMAL" property="total"/> <result column="used" jdbcType="DECIMAL" property="used"/> <result column="residue" jdbcType="DECIMAL" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, user_id, total, used, residue </sql> <update id="decrease"> update t_account set used = used + #{money}, residue=residue - #{money} where user_id = #{userId} </update> </mapper>增加服务接口public interface IAccountService { int decrease( Long userId, BigDecimal money); }增加服务实现这里注意以下,我们模拟了报错信息@Service @Slf4j public class AccountServiceImpl implements IAccountService { @Resource private AccountDao accountDao; @Override // @Transactional public int decrease(Long userId, BigDecimal money) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); //模拟报错 int age = 10 / 0; return accountDao.decrease(userId, money); } }增加接口controller@RestController @Slf4j @RequestMapping("/account") public class AccountController { @Resource private IAccountService accountService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) { accountService.decrease(userId, money); return new CommonResult(200, "账户扣减成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"net.xiangcaowuyu.springcloud.alibaba.seata.dao"}) @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class SeataAccountServiceMain2003 { public static void main(String[] args) { SpringApplication.run(SeataAccountServiceMain2003.class, args); } }测试通过postman测试可以看到系统抛出了异常,然后我们查看以下数据库,可以看到数据没有发生任何变化。
2021年07月26日
1,031 阅读
0 评论
1 点赞
2021-07-26
Seata基础教程上-seata 1.4.2的安装及基于Nacos的配置
先吐槽一下,seata的文档真的是乱的一批。我们这里只介绍安装及基本使用过程。1.seata介绍Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。具体原理可以参考官网http://seata.io/zh-cn/docs/overview/what-is-seata.html。[alt type="success"]一定要注意版本问题,包括安装的Nacos版本、Seata版本、Spring Cloud版本、Spring Boot版本等等。[/alt]2.前置条件2.1安装JDK并配置环境变量安装JDK1.8及以上版本,并正确配置环境变量。2.2maven我这里用的是3.6.3,并配置阿里云仓库。2.3Git由于需要将Seata Server的配置文件导入Nacos,所以需要安装Git2.4安装NacosNacos 2.0.2版本,关于Nacos的安装,我们稍后会出一个专门的文章进行介绍。[tag type="warning"]温馨提示[/tag]所以学习本篇文章之前,您至少应该改知道Nacos的安装以及在Spring Cloud中如何利用Nacos进行服务的注册与发现。Nacos默认端口为8848,由于我这里配置了集群,所以前端端口使用的是1111,大家在下面配置文件注意以下。3.seata安装我这里使用的seata是1.4.2(截止到目前最新的版本)。[btn href="http://seata.io/zh-cn/blog/download.html" type="default"]Seata下载地址[/btn]3.1.修改配置文件3.1.1.修改file.conf进入Seata的conf目录,找到file.conf,如下图修改以下内容## transaction log store, only used in seata-server store { ## store mode: file、db、redis mode = "db" #我们用数据库存储,改成db ## rsa decryption public key publicKey = "" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" # 改成MySql driverClassName = "com.mysql.jdbc.Driver" ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param url = "jdbc:mysql://ip:3306/数据库名称?rewriteBatchedStatements=true" user = "数据库用户" password = "数据库密码" minConn = 5 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { ## redis mode: single、sentinel mode = "single" ## single mode property single { host = "127.0.0.1" port = "6379" } ## sentinel mode property sentinel { masterName = "" ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381" sentinelHosts = "" } password = "" database = "0" minConn = 1 maxConn = 10 maxTotal = 100 queryLimit = 100 } } 3.1.2.修改registry.conf进入Seata的conf目录,找到registry.conf,如下图我们这里用Nacos,所以主要是配置Nacos的信息,配置内容包括两部分,registry服务,将type从file改成nacos,将seata服务配置进nacos;config将type从file改成nacos,这样不需要每个项目都放file.conf修改内容如下:registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" #改成Nacos nacos { application = "seata-server" serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 group = "SEATA_GROUP" #Nacos分组 namespace = "" username = "nacos" #Nacos用户名 password = "nacos" #Nacos密码 } eureka { serviceUrl = "http://localhost:8761/eureka" application = "default" weight = "1" } redis { serverAddr = "localhost:6379" db = 0 password = "" cluster = "default" timeout = 0 } zk { cluster = "default" serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } consul { cluster = "default" serverAddr = "127.0.0.1:8500" aclToken = "" } etcd3 { cluster = "default" serverAddr = "http://localhost:2379" } sofa { serverAddr = "127.0.0.1:9603" application = "default" region = "DEFAULT_ZONE" datacenter = "DefaultDataCenter" cluster = "default" group = "SEATA_GROUP" addressWaitTime = "3000" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul、etcd3 type = "nacos" #改成Nacos nacos { serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 namespace = "" group = "SEATA_GROUP" #Nacos分组 username = "nacos" #Nacos用户名 password = "nacos" #Nacos密码 # dataId = "" } consul { serverAddr = "127.0.0.1:8500" aclToken = "" } apollo { appId = "seata-server" ## apolloConfigService will cover apolloMeta apolloMeta = "http://192.168.1.204:8801" apolloConfigService = "http://192.168.1.204:8080" namespace = "application" apolloAccesskeySecret = "" cluster = "seata" } zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" nodePath = "/seata/seata.properties" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } } 3.1.3.导入Seata相应的配置项到Nacos的配置中心(非注册中心)由于我这里注册中心跟配置中心用的一个,所以我其实导入的就是一个Nacos。3.1.3.1.创建导入脚本在conf文件夹下,创建nacos-config.sh,并将以下内容粘贴进去#!/usr/bin/env bash # Copyright 1999-2019 Seata.io Group. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at、 # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. while getopts ":h:p:g:t:u:w:" opt do case $opt in h) host=$OPTARG ;; p) port=$OPTARG ;; g) group=$OPTARG ;; t) tenant=$OPTARG ;; u) username=$OPTARG ;; w) password=$OPTARG ;; ?) echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1 ;; esac done urlencode() { for ((i=0; i < ${#1}; i++)) do char="${1:$i:1}" case $char in [a-zA-Z0-9.~_-]) printf $char ;; *) printf '%%%02X' "'$char" ;; esac done } if [[ -z ${host} ]]; then host=localhost fi if [[ -z ${port} ]]; then port=8848 fi if [[ -z ${group} ]]; then group="SEATA_GROUP" fi if [[ -z ${tenant} ]]; then tenant="" fi if [[ -z ${username} ]]; then username="" fi if [[ -z ${password} ]]; then password="" fi nacosAddr=$host:$port contentType="content-type:application/json;charset=UTF-8" echo "set nacosAddr=$nacosAddr" echo "set group=$group" failCount=0 tempLog=$(mktemp -u) function addConfig() { curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [[ -z $(cat "${tempLog}") ]]; then echo " Please check the cluster status. " exit 1 fi if [[ $(cat "${tempLog}") =~ "true" ]]; then echo "Set $1=$2 successfully " else echo "Set $1=$2 failure " (( failCount++ )) fi } count=0 for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do (( count++ )) key=${line%%=*} value=${line#*=} addConfig "${key}" "${value}" done echo "=========================================================================" echo " Complete initialization parameters, total-count:$count , failure-count:$failCount " echo "=========================================================================" if [[ ${failCount} -eq 0 ]]; then echo " Init nacos config finished, please start seata-server. " else echo " init nacos config fail. " fi3.1.3.2.创建配置文件在conf的上级目录,创建config.txt并粘贴以下内容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.shutdown.wait=3 service.vgroupMapping.my_tx_group=default #自定义,一定注意与spring cloud配置文件保持一致 service.default.grouplist=127.0.0.1:8091 #seata服务 service.enableDegrade=false service.disableGlobalTransaction=false client.rm.asyncCommitBufferLimit=10000 client.rm.lock.retryInterval=10 client.rm.lock.retryTimes=30 client.rm.lock.retryPolicyBranchRollbackOnConflict=true client.rm.reportRetryCount=5 client.rm.tableMetaCheckEnable=false client.rm.tableMetaCheckerInterval=60000 client.rm.sqlParserType=druid client.rm.reportSuccessEnable=false client.rm.sagaBranchRegisterEnable=false client.rm.tccActionInterceptorOrder=-2147482648 client.tm.commitRetryCount=5 client.tm.rollbackRetryCount=5 client.tm.defaultGlobalTransactionTimeout=60000 client.tm.degradeCheck=false client.tm.degradeCheckAllowTimes=10 client.tm.degradeCheckPeriod=2000 client.tm.interceptorOrder=-2147482648 store.mode=db store.lock.mode=db store.session.mode=db store.publicKey= store.file.dir=file_store/data store.file.maxBranchSessionSize=16384 store.file.maxGlobalSessionSize=512 store.file.fileWriteBufferCacheSize=16384 store.file.flushDiskMode=async store.file.sessionReloadReadSize=100 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://数据库IP:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user=数据库用户名 store.db.password=数据库密码 store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000 store.redis.mode=single store.redis.single.host=127.0.0.1 store.redis.single.port=6379 store.redis.sentinel.masterName= store.redis.sentinel.sentinelHosts= store.redis.maxConn=10 store.redis.minConn=1 store.redis.maxTotal=100 store.redis.database=0 store.redis.password= store.redis.queryLimit=100 server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTimeoutUnlockEnable=false server.distributedLockExpireTime=10000 client.undo.dataValidation=true client.undo.logSerialization=jackson client.undo.onlyCareUpdateColumns=true server.undo.logSaveDays=7 server.undo.logDeletePeriod=86400000 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k log.exceptionRate=100 transport.serialization=seata transport.compressor=none metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898上图一定要注意配置数据库信息及service.vgroupMapping.my_tx_group=default,这个后续在spring cloud项目的配置文件中会用到。3.1.3.3.导入配置文件打开git bash,进入到conf目录中,执行以下命令sh [nacos-config.sh文件路径] -h [nacos-ip地址] -p 8848 -g SEATA_GROUP[导入的组] -u [用户名] -w [密码] 执行完成后,查看我们Nacos配置中心中,配置文件是否已存在。3.1.4.创建相关的数据库(在回滚或提交前会将日志保存在数据库中,成功后会删除)3.1.3.1.创建seata数据库,并预置数据这个数据库就是我们上面配置文件中使用到的数据库,脚本如下-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTS `global_table` ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;3.1.4.2.创建undo_log表注意:在业务数据库中创建undo_log,与上面的脚本不在一个数据库中。比如我有三个微服务,三个数据库,那么就需要在三个业务数据库中创建undo_log表-- 在业务数据库中创建undo_log(与上面的脚本不在一个数据库中) CREATE TABLE IF NOT EXISTS `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(6) NOT NULL COMMENT 'create datetime', `log_modified` DATETIME(6) 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';4.运行seata运行Nacos这里不多介绍。运行seata进入seata的bin目录,执行./seata-server.bat,运行成功后,查看Nacos的服务列表能看到我们配置的seata即代表成功。
2021年07月26日
1,209 阅读
0 评论
0 点赞