前言:关于nacos与seata以及springboot的基本配置与pom文件整合请看上篇文章 nacos1.2.0整合seata1.2.0最新版
这里直接介绍具体使用方法:
1.结构大致如下
2.测试controller
package com.vincent.spring_cloud_seata_order.Controller;
import com.vincent.spring_cloud_seata_order.Service.OrderService;
import com.vincent.spring_cloud_seata_order.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.math.BigDecimal;
import java.util.Date;
@Controller
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping(value = "/test",method = RequestMethod.GET)
@ResponseBody
public Integer test(){
//限流
try {
orderService.beginOrder();
return 1;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
}
3.测试feign,这里调度是另一个服务goods服务,goods也是简单的访问新增处理
package com.vincent.spring_cloud_seata_order.Feign;
import com.vincent.spring_cloud_seata_order.Feign.Fallback.GoodsFeignRollBack;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "goods",fallback = GoodsFeignRollBack.class)
public interface GoodsFeign {
@RequestMapping(value = "/goods/test",method = RequestMethod.GET)
public String test();
// @RequestMapping(value = "/goods/searchGoods",method = RequestMethod.GET)
// public String SearchGoods(@RequestParam("goodsId") Integer goodsId);
}
3.1 goods服务的controller代码
package com.vincent.spring_cloud_seata_goods.Controller;
import com.vincent.spring_cloud_seata_goods.Serivce.GoodsService;
import com.vincent.spring_cloud_seata_goods.Util.Result;
import com.vincent.spring_cloud_seata_goods.entity.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@RequestMapping("/test")
@ResponseBody
public Integer test(){
//限流
try {
Goods goods=new Goods();
goods.setName("测试商品"+ UUID.randomUUID());
goods.setCreateTime(new Date());
goodsService.save(goods);
return goods.getId();
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
}
4.Aop切面处理类
package com.vincent.spring_cloud_seata_order.Util;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class FeignTransactionalAop {
//访问方法前进入处理
@Before("execution(* com.vincent.spring_cloud_seata_order.Service.*.*(..))")
public void before(JoinPoint joinPoint) throws TransactionException {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("拦截到需要分布式事务的方法"+method.getName());
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
// 超时时间 , 所在服务
tx.begin(300000, "order");
}
//抛出异常后加入处理,手动回滚全局事务
@AfterThrowing(throwing = "e", pointcut = "execution(* com.vincent.spring_cloud_seata_order.Service.*.*(..))")
public void doRecoveryActions(Throwable e) throws TransactionException {
System.out.println("方法执行异常");
if (!StringUtils.isBlank(RootContext.getXID())) {
GlobalTransactionContext.reload(RootContext.getXID()).rollback();
}
}
}
5.测试service类
package com.vincent.spring_cloud_seata_order.Service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vincent.spring_cloud_seata_order.Feign.GoodsFeign;
import com.vincent.spring_cloud_seata_order.Service.OrderService;
import com.vincent.spring_cloud_seata_order.entity.Order;
import com.vincent.spring_cloud_seata_order.mapper.obj.OrderMapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.UUID;
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
private GoodsFeign goodsFeign;
@Autowired
private OrderMapper orderMapper;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public String beginOrder() {
Order order=new Order();
order.setName("测试订单:"+ UUID.randomUUID());
order.setOriginalPrice(new BigDecimal("100"));
System.out.println("订单服务开始插入数据");
orderMapper.insert(order);
System.out.println("开始调度商品服务插入test");
String goodsMsg=goodsFeign.test();
//seata当处理完多个并行服务时候,会对seata数据库3表做操作,也同对并行服务undo_log表进行操作
if(!goodsMsg.equals("熔断器触发")) {
System.out.println("模拟测试熔断器");
throw new RuntimeException();
}
return "success";
}
}
6.访问测试order服务 http://localhost:11100/order/test
并且debug端点卡住
8.yml文件
server:
port: 11100
spring:
application:
name: order
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.116.130:3306/order?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
cloud:
nacos:
discovery:
server-addr: http://192.168.116.130:8848
alibaba:
seata:
tx-service-group: order-service-group
##seata配置,相当于registry.conf
seata:
client:
support:
spring:
datasource-autoproxy: true
tx-service-group: order-service-group
service:
disable-global-transaction: false
grouplist:
default: 192.168.116.130:8091
registry:
type: nacos
nacos:
server-addr: 192.168.116.130
config:
nacos:
server-addr: 192.168.116.130
type: nacos
##设置feign超时时间,否则时间过短,造成数据不一致问题
ribbon:
ConnectTimeout: 10000 # ribbon链接超时时长
ReadTimeout: 10000 # ribbon读取超时时间长
mybatis-plus:
# configuration:mybatis.mapper-locations=classpath:mapper/*.xml
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:/mybatis/mappers/obj/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.vincent.spring_cloud_seata_order.entity
global-config:
# 数据库相关配置
db-config:
#主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: UUID
#字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
field-strategy: not_empty
#驼峰下划线转换
column-underline: true
#数据库大写下划线转换
#capital-mode: true
# 逻辑删除配置
logic-delete-value: 0
logic-not-delete-value: 1
db-type: mysql
#刷新mapper 调试神器
refresh: true
management:
endpoints:
web:
exposure:
include: "*"
9.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-parent</artifactId>-->
<!-- <version>2.1.6.RELEASE</version>-->
<!-- <relativePath>../spring_cloud_alibaba_dependence/pom.xml</relativePath>-->
</parent>
<groupId>com.vincent</groupId>
<artifactId>spring_cloud_seata_order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_cloud_seata_order</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<nacos.version>2.2.1.RELEASE</nacos.version>
<!-- <boot.version>2.2.6.RELEASE</boot.version>-->
<feign.version>2.2.2.RELEASE</feign.version>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-parent</artifactId>-->
<!-- <version>2.1.7.RELEASE</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Seata 包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.1.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
<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.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- <version>${boot.version}</version>-->
</dependency>
<!--openfeign 支持解析MVC注解接口-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${feign.version}</version>
</dependency>
<!--Nacos配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>-->
<!-- <version>${nacos.version}</version>-->
<!-- </dependency>-->
<!--ORM-->
<!-- 集成mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
<exclusions>
<exclusion>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
10.查询seata服务表的数据,
11.查询order业务库与goods业务库的unlock表
12.再次查询业务库里业务数据是否正常,发现此时两边业务库数据都输入正常了
13.由于我在代码最后面手动抛出了运行异常,然后由aop负责截取进行手动处理
14.于是乎数据库显示如下,发现两业务表数据都已经回滚成功了,说明全局事务真正生效了