# spring-cloud-nacos
**Repository Path**: qwzcys/spring-cloud-nacos
## Basic Information
- **Project Name**: spring-cloud-nacos
- **Description**: springcloud整合nacos
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2020-11-18
- **Last Updated**: 2021-07-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 项目说明
spring-cloud整合nacos,实现动态配置中心、心跳检测、feign、zuul网关静态路由以及服务熔断、seata分布式事务控制
## 项目结构
```
├─load-balancing feign以及负载均衡
├─logs 日志
├─nacos-client-payment 服务提供者、rocketmq生产者
├─nacos-common 通用工具模块
├─nacos-gateway-zuul zuul网关
├─rocketmq-client rocketmq消费者
```
## 服务列表

## 功能实现
### 服务注册与发现
#### 1、新建配置文件

nacos管理端——配置信息——配置管理——新建配置
####2、引入依赖
```
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.1.0.RELEASE
pom
import
com.alibaba.nacos
nacos-client
com.alibaba.nacos
nacos-client
1.4.0
```
#### 3、bootstrap.yml
```
server:
port: 8090 #端口号
spring:
application:
name: nacos_client-dev-yaml #服务名,同时也是nacos配置文件名
#添加nacos-server框架
cloud:
nacos:
config:
server-addr: 106.53.22.186:8848 #nacos配置中心地址
file-extension: yml
```
#### 4、启动项目
```
@EnableDiscoveryClient
@SpringBootApplication
@MapperScan({"com.nacos.payment.dao.*"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
```
### 整合feign调用服务
#### 1、新建配置文件

#### 2、引入依赖
```
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2.1.1.RELEASE
```
#### 3、bootstrap.yml配置
```
server:
port: 8095
spring:
application:
name: load_balancing-dev-yaml
cloud:
nacos:
discovery:
server-addr: 106.53.22.186:8848
```
##### 注意,feign与服务注册的格式不同,服务注册的格式为——
```
spring:
application:
name: nacos_client-dev-yaml #服务名,同时也是nacos配置文件名
#添加nacos-server框架
cloud:
nacos:
config:
server-addr: 106.53.22.186:8848 #nacos配置中心地址
file-extension: yml
```
feign格式为
```
spring:
application:
name: load_balancing-dev-yaml
cloud:
nacos:
discovery:
server-addr: 106.53.22.186:8848
```
两者的区别主要体现在
```
服务注册
cloud:
nacos:
config:
server-addr: 106.53.22.186:8848 #nacos配置中心地址
file-extension: yml
feign
cloud:
nacos:
discovery:
server-addr: 106.53.22.186:8848
```
#### 4、调用服务
```
@FeignClient(value = "nacos-client-payment")
@Component
public interface TestService {
/**
* 访问test接口
* @return
*/
@GetMapping("/test/test")
public BaseResult test();
}
```
#### 5、写接口
```
@RestController
@RequestMapping("test")
public class PaymentController {
@Autowired
private TestService testService;
private static final Logger log = LoggerFactory.getLogger(PaymentController.class);
@GetMapping("test")
public BaseResult test(){
log.info("访问test");
testService.test();
return BaseResult.successResult("访问test");
}
}
```
#### 6、启动项目
```
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
```
#### 7、Feign+Hystrix熔断降级
##### 6、application.yml开启
```
feign:
hystrix:
enabled: true
```
##### feignClient注解加上fallbackFactory
```
@FeignClient(value = "nacos-client-payment",fallbackFactory = MyFallBack.class)
```
##### MyFallBack方法
```
@Component
public class MyFallBack implements FallbackFactory {
private final TestService testService;
public MyFallBack(){
this.testService=new TestService() {
@Override
public BaseResult test() {
return BaseResult.errorResult("触发熔断");
}
@Override
public BaseResult towTest() {
return BaseResult.errorResult("触发熔断");
}
};
}
@Override
public TestService create(Throwable cause) {
return testService;
}
}
```
#### sentinel规则持久化
##### 1、nacos建立持久化规则文件——load-balancing-test
文件内容
```
[
{
"resource":"/test/test", 资源名
"limitApp":"default", #default 任何人都可以调用此规则,
"grade":1, #限流阈值类型(1=QPS,0=并发线程数)
"count":0, #限流阈值
"strategy":0, #调用关联策略(0=资源本身,1=关联资源,2=链路路口)
"controlBenhavior":0, #流量控制效果(0=直接拒绝,1=warm up,2=匀速排队)
"clusterMode":false #是否集群
}
]
```
##### 2、服务导入依赖
```
com.alibaba.csp
sentinel-datasource-nacos
```
##### 3、application.yml配置
```
spring:
cloud:
sentinel:
eager: true #是否开启饥饿加载 默认为false不开启
datasource:
balancing:
nacos:
server-addr: 106.53.22.186:8848
dataId: load-balancing-test
groupId: BALANCING_GROUP
ruleType: flow #flow=流控规则,degrade=熔断规则
usename: nacos
passowrd: nacos123321
```
#### sentinel熔断和流控
##### 1、添加注解
```
@SentinelResource(value = "balancing-test",blockHandlerClass = MyFallBack.class,blockHandler = "blockHandler")
public BaseResult test(Long time) {
BaseResult test = testService.test();
try {
log.info("开始休眠");
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
return test;
}
```
##### 注意,熔断的方法参数必须与接口参数一致
##### 2、实现方法
```
public static BaseResult blockHandler(Long time,BlockException e){
return BaseResult.errorResult("触发流控");
}
```
### 整合zuul
#### 1、添加依赖
```
org.springframework.cloud
spring-cloud-starter-netflix-zuul
org.springframework.boot
spring-boot-starter-actuator
org.example
nacos-common
1.0-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-discovery
0.2.1.RELEASE
com.alibaba.nacos
nacos-client
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-config
0.2.1.RELEASE
com.alibaba.nacos
nacos-client
com.alibaba.nacos
nacos-client
1.4.0
```
#### 2、添加配置信息文件
nacos_gateway_zuul-dev.yaml
```
spring:
application:
name: nacos_gateway_zuul
zuul:
# include-debug-header: true
#禁用这个微服务microservicecloud-dept
#ignored-services: microservicecloud-dept
#网关前缀
# prefix: /atguigu
#禁用所有的微服务
# ignored-services: "*"
routes:
# payment:
# #配置路由名称对应的微服务名称,也就是说,当访问/nacos-client-payment/**就相当于文nacos-client-payment/**
# service-id: nacos-client-payment
# path: /payment/**
# retryable: true
# strip-prefix: true
baidu:
path: /baidu/**
url: http://www.baidu.com/
# retryable: true
# stripPrefix: true
```
#### 3、bootstrap设置
```
server:
port: 8060
spring:
application:
name: nacos_gateway_zuul-dev
cloud:
nacos:
config:
server-addr: 106.53.22.186:8848
file-extension: yaml
group: DEFAULT_GROUP
discovery:
server-addr: 106.53.22.186:8848
```
#### 4、启动类
```
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
```
##### 动态刷新路由(配置文件的方式)
```
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@RefreshScope
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
```
#### 5、服务熔断
```
@Component
public class ServiceFallbackProvider implements FallbackProvider {
private static final Logger log = LoggerFactory.getLogger(ServiceFallbackProvider.class);
@Autowired
private RouteLocator routeLocator;
@Autowired
private UrlPathHelper urlPathHelper;
@Override
public String getRoute() {
return null;
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
/**
* ClientHttpResponse的fallback的状态码
* @return
* @throws IOException
*/
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
/**
* ClientHttpResponse的fallback的状态码
* @return
* @throws IOException
*/
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
/**
* ClientHttpResponse的fallback的状态码
* @return
* @throws IOException
*/
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
/**
* 设置响应体
* @return
* @throws IOException
*/
@Override
public InputStream getBody() throws IOException {
final RequestContext ctx = RequestContext.getCurrentContext();
final Route route = getRoute(ctx.getRequest());
log.error("触发熔断"+route.getLocation());
return new ByteArrayInputStream("服务异常请稍后访问".getBytes());
}
/**
* 设置表头
* @return
*/
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
MediaType mt = new MediaType("application", "json", Charset.forName("utf-8"));
headers.setContentType(mt);
return headers;
}
};
}
/**
* 获取路由信息
* @param request
* @return
*/
private Route getRoute(HttpServletRequest request){
final String requestUri = urlPathHelper.getRequestUri(request);
final Route route = routeLocator.getMatchingRoute(requestUri);
log.info("路由配置完整路径:"+route.getFullPath());
return route;
}
}
```
### 整合rocketmq
#### 消息生产者
##### 导入依赖
```
org.apache.rocketmq
rocketmq-spring-boot-starter
2.0.3
```
注:springcloud-alibaba最新版只支持到rocketmq4.4.0版本
##### 发送消息实现
```
DefaultMQProducer producer = new DefaultMQProducer("bbcOrder-group");
producer.setNamesrvAddr("localhost:9876");
producer.setSendMessageWithVIPChannel(false);
producer.setInstanceName(System.currentTimeMillis()+"");
producer.start();
System.out.println(producer.getCreateTopicKey());
Message msg = new Message(rocketTopic
,JacksonUtils.javaBeanForjson(bbcOrder).getBytes() // Message body
);
//异步发送
producer.send(msg,RocketMqSendCallback.sendCallback(),5000);
//同步发送
producer.send(msg);
```
注:不添加属性producer.setInstanceName(System.currentTimeMillis()+"");
会报错No route info of this topic
##### 异步发送验证类
```
public class RocketMqSendCallback {
private static final Logger log = LoggerFactory.getLogger(RocketMqSendCallback.class);
public static SendCallback sendCallback(){
return new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("成功发送消息:"+sendResult.toString());
}
@Override
public void onException(Throwable throwable) {
log.error("发送消息失败:"+throwable);
}
};
}
}
```
#### 消息消费者
##### pom导入
```
org.apache.rocketmq
rocketmq-spring-boot-starter
2.0.3
```
##### 实现类
```
@Component
@RocketMQMessageListener(topic = "bbcOrder-topic",consumerGroup = "bbcOrder-group-consume",nameServer = "localhost:9876")
public class OrderConsume implements RocketMQListener {
private final Logger logger =
LoggerFactory.getLogger(RocketMQListener.class);
@Override
public void onMessage(String s) {
logger.info("接收到消息:"+s);
}
}
```
### 整合seata(用nacos持久化seata配置)
#### 对nacos录入配置文件并持久化(seata server端)
##### 创建文件config.txt并录入内容
```
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
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_test_tx_group=default
service.default.grouplist=127.0.0.1:8091
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.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
store.mode=file
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://312:3306/324?useUnicode=true
store.db.user=4
store.db.password=fdg
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.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
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
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
log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
```
##### 运行shell文件方法
```
sh nacos-config.sh -h nacos的Ip地址 nacos的命名空间 -p nacos的端口号 -u nacos用户名 -w nacos密码 -g 本配置列表的所属分组(要与后面seata服务端config.nacos.group保持一致,默认为SEATA_GROUP)
```
###### 示例
```
sh nacos-config.sh -h 106.53.22.186 -p 8848 50edd814-d2c6-4a27-b633-93d17a9aaba6 -u nacos -w nacos123321 -g SEATA_GROUP
```
##### 运行python文件方法
```
python nacos-config.py 106.53.22.186:8848 50edd814-d2c6-4a27-b633-93d17a9aaba6
```
```
python nacos-config.py nacos的ip:nacos的端口号 nacos命名空间id
```
##### 启动命令
```
直接运行{seata}/bin/seata-server.sh [options](linux)或者{seata}/bin/seata-server.bat [options]
Options:
--host, -h
The host to bind.
Default: 0.0.0.0
--port, -p
The port to listen.
Default: 8091
--storeMode, -m
log store mode : file、db
Default: file
--help
```
```
-h: 注册到注册中心的ip
-p: Server rpc 监听端口
-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
```
###### 示例
```
./seata-server.sh -h 106.53.22.186 -p 8700 -m db
```
#### seata client端
##### 项目中必须连接数据库
##### 数据库连接配置
###### application.yml
```
spring:
datasource:
url: jdbc:mysql://106.53.22.186:3306/seate?useUnicode=true&useSSL=false
username: admin321
password: admin123
driver-class-name: com.mysql.cj.jdbc.Driver
platform: mysql
type: com.alibaba.druid.pool.DruidDataSource
```
###### pom.xml
```
mysql
mysql-connector-java
8.0.11
com.alibaba
druid
1.1.20
```
###### 排除springboot自动装配
```
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
```
###### 数据库代理配置类
```
@Configuration
public class SeataDataSourceConfig {
/**
* @param sqlSessionFactory SqlSessionFactory
* @return SqlSessionTemplate
*/
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 从配置文件获取属性构造datasource,注意前缀,这里用的是druid,根据自己情况配置,
* 原生datasource前缀取"spring.datasource"
*
* @return
*/
@Bean(name = "druidDataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
/**
* 构造datasource代理对象,替换原来的datasource
* @param druidDataSource
* @return
*/
@Primary
@Bean("dataSource")
public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSourceProxy);
SqlSessionFactory factory = null;
try {
factory = bean.getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
return factory;
}
/**
* MP 自带分页插件
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor page = new PaginationInterceptor();
page.setDialectType("mysql");
return page;
}
}
```
##### 整合seata使用配置
###### 1、在nacos插入service.vgroupMapping.${tx-service-group},配置文件,内容为default
###### 2、导入pom
```
io.seata
seata-spring-boot-starter
1.2.0
pom
com.alibaba.cloud
spring-cloud-alibaba-seata
2.2.0.RELEASE
io.seata
seata-spring-boot-starter
io.seata
seata-spring-boot-starter
1.2.0
```
###### 3、application.yml配置
```
seata:
enabled: true
application-id: seataSserv
tx-service-group: nacos-client-payment #此处配置自定义的seata事务分组名称
enable-auto-data-source-proxy: true #开启数据库代理
service:
vgroup-mapping:
nacos-client-payment: default
config:
type: nacos
nacos:
namespace: a392391d-f5b5-4b82-a42b-3ff9fdb7d573
server-addr: 106.53.22.186:8848
group: DEFAULT_GROUP
username: nacos
password: nacos123321
registry:
type: nacos
nacos:
application: seataServ
server-addr: 106.53.22.186:8848
namespace: a392391d-f5b5-4b82-a42b-3ff9fdb7d573
username: nacos
password: nacos123321
```
###### 4、添加文件file.conf、registry.conf
###### registry内容为
```
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
loadBalance = "RandomLoadBalance"
loadBalanceVirtualNodes = 10
nacos {
application = "seataServ"
serverAddr = "106.53.22.186:8848"
group = "DEFAULT_GROUP"
namespace = "a392391d-f5b5-4b82-a42b-3ff9fdb7d573"
cluster = "default"
username = "nacos"
password = "nacos123321"
}
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"
}
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 {
serverAddr = "106.53.22.186:8848"
namespace = "a392391d-f5b5-4b82-a42b-3ff9fdb7d573"
group = "DEFAULT_GROUP"
username = "nacos"
password = "nacos123321"
application = "seataServ"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
namespace = "application"
apolloAccesskeySecret = ""
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
```
注:nacos包含的"namespace"字段值为nacos命名空间的id而非命名空间名
file.conf内容为
```
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "file"
## 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"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://106.53.22.187:3306/seate"
user = "admin321"
password = "admin123"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {
host = "127.0.0.1"
port = "6379"
password = ""
database = "0"
minConn = 1
maxConn = 10
maxTotal = 100
queryLimit = 100
}
}
service {
#transaction service group mapping
vgroupMapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "106.53.22.186:8700"
}
```
##### 使用
###### 注入配置
```
@GetMapping("createOrder")
@GlobalTransactional(name = "createOrder",rollbackFor = Exception.class)
public BaseResult createOrder(){
Order order = new Order();
PurchaseRecords purchaseRecords = new PurchaseRecords();
order.setName("a");
order.setOrderNumber("sdonjfuoisdf");
testService.createOrder(order);
purchaseRecords.setGoodsCount(1);
purchaseRecords.setGoodsId(2);
purchaseRecords.setUserName("sdf");
purchaseRecords.setUserId(2);
testService.createPurchase(purchaseRecords);
return BaseResult.successResult();
}
```
注:rollbackFor为必需品,否则seata会报错Could not register branch into global session xid = 106.53.22.186:8091:2030811323 status = Rollbacking whil