目录
十、Sentinel服务熔断+Ribbon+fallback+blockHandler
十三、SpringCloud Alibaba Seata处理分布式事务
一、SpringCloud Alibaba
将模块置于维护模式,意味着 Spring Cloud 团队将不会再向模块添加新功能。
我们将修复 block 级别的 bug 以及安全问题,我们也会考虑并审查社区的小型 pull request。
新组件功能将以其他替代平代替的方式实现
1、SpringCloud Alibaba能做什么
服务限流降级:
默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:
适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
分布式配置管理:
支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:
基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
阿里云对象存储:
阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:
提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
中文资料:
spring-cloud-alibaba/README-zh.md at master · alibaba/spring-cloud-alibaba · GitHub
二、Nacos服务注册和配置中心
1、简介
Nacos: Dynamic Naming and Configuration Service
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos就是注册中心 + 配置中心的组合等价于 Eureka+Config +Bus
各种注册中心比较
官方文档
Spring Cloud Alibaba Reference Documentation
2、安装并运行
进入下载的nacos文件夹
->bin
如果在windows环境下运行,选择startup.cmd,linux选择startup.sh
运行,nacos默认占用8848端口
访问http://localhost:8848/nacos/进入管理页面
默认用户名密码都是nacos
3、作为服务发现中心---生产者
新建模块
1、pom,配置文件
父pom
<!--SpringCloudAlibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
pom
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置文件
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
2、@EnableDiscoveryClient
主启动类加上该注解,开启服务发现
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class,args);
}
}
3、业务类
Controller
package com.atguigu.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id)
{
return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
}
}
启动
成功
4、仿照9001,配置一台9002
虚拟端口映射法:
手动搭建9002,启动
4、作为服务发现中心---消费者
新建模块
1、pom,yml
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
2、主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain83.class,args);
}
}
3、业务类
config
package com.atguigu.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
Controller
package com.atguigu.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class OrderNacosController
{
@Resource
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping("/consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") Long id)
{
return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
}
}
5、作为服务发现中心---测试
启动
测试访问http://localhost:83/consumer/payment/nacos/1
调用成功,再次调用,会发现Nacos默认遵循负载均衡规则
nacos自身集成了Ribbon
5、Nacos和CAP
Nacos服务发现实例模型
何时选择使用何种模式?
一般来说,
如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring cloud 和 Dubbo 服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S服务和DNS服务则适用于CP模式。
CP模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
6、作为服务配置中心--基础配置
新建模块
1、pom
<dependencies>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、yml
Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,
拉取配置之后,才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
bootstrap.yml
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
application.yml
spring:
profiles:
active: dev # 表示开发环境
3、主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377 {
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class,args);
}
}
4、业务类
Controller
package com.atguigu.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope//Spring原生注解支持动态刷新
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
@RefreshScope
SpringCloud 的原生注解,实现了配置的自动更新功能
5、配置配置中心文件
nacos官网文档中,给出了配置文件的格式
${spring.application.name}-${spring.profiles.active}.${file-extension}
根据我们的配置文件,得到最后的结果
6、在nacos管理页面新建配置
发布
测试
支持动态刷新
7、作为服务配置中心--分类配置
问题1:
实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境。
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?
问题2:
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、
1、Namespace+Group+Data ID
类似Java里面的package名和类名,最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
默认情况下
Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT
Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去
Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,
这时就可以给杭州机房的Service微服务起一个集群名称(HZ),
给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
最后是Instance,就是微服务的实例。
2、Nacos的DataId
新建一个测试环境配置文件
需要切换环境,只需要改变application.yml中配置文件配置的环境
spring:
profiles:
# active: dev # 表示开发环境
active: test # 表示测试环境
测试
3、Nacos的GroupId
默认情况
新建一个配置文件,放入两个不同组中
在配置文件中声明开发环境和组别,访问Controller
4、Nacos的Namespace
点击新建 命名空间
回到服务列表,在test命名空间中建立一个配置文件,组别为HY_GROUP
修改配置文件即可读取,命名空间编号如下:
修改配置
测试
成功
三、Nacos集群和持久化(★)
1、Nacos官网对于集群的说明
SLB模块做到负载均衡,即(Server Load Balance)
分解图如下
2、预备环境
- 64 bit OS Linux/Unix/Mac,推荐使用Linux系统。
- 64 bit JDK 1.8+;
- Maven 3.2.x+;
- 3个或3个以上Nacos节点才能构成集群。
2、Nacos的默认持久化策略
Nacos默认自带的是嵌入式数据库derby,详情见github中的源码,默认仅支持mysql作为外部数据库
nacos/pom.xml at develop · alibaba/nacos · GitHub
1、如何从derby切换为mysql
注意:8.0以上mysql需要重新编译jar包
执行nacos/conf文件夹下的sql文件,在mysql中创建表
注意,Nacos官网推荐使用nacos_config作为数据库名
执行完sql文件后,找到sql文件所在文件夹下的application.properties文件,编辑
加入配置如下
##########################mysql######################
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
###DB Count
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=root
然后重启nacos
即可替换为mysql
3、Linux下部署Nacos集群
1、下载并解压nacos
解压命令
tar -zxvf nacos压缩文件
2、运行conf文件下的sql文件,搭建mysql数据库
3、修改conf下的cluster文件,配置三台集群
4、修改startup.sh文件,让其可以指定启动端口
4、配置Nginx
docker启动nginx,修改/etc/nginx/nginx.conf文件
监听1111端口
#gzi on
.......
upstream cluster{
server 127.0.0.1:3333;
server 127.0.0.1:4444;
server 127.0.0.1:5555;
}
server {
listen 1111;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
#root html;
#index index.html index.htm;
proxy_pass http://cluster;
}
.......省略
按如下步骤启动
指定配置文件启动
./nginx -c /usr/local/nginx/conf/nginx.conf
查看nacos的集群数量,为3,然后访问nginx,跳转至nacos登录,集群成功!
四、SpringCloud Sentinel(熔断限流)
官网说明
Sentinel的主要特性
可以解决如下问题
1、安装Sentinel控制台
下载地址
Releases · alibaba/Sentinel · GitHub
java -jar sentinel-dashboard-1.8.2.jar
启动
默认用户名密码都是sentinel,默认端口8080
2、初始化演示工程
新建模块
1、pom,配置文件
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
2、业务类
package com.atguigu.springcloud.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class FlowLimitController { @GetMapping("/testA") public String testA() { return "------testA"; } }
Sentinel采用懒加载机制,需要执行业务类 ,才会对服务进行监控
3、运行模块
监控页面:
五、Sentinel流控规则
1、流控规则
解释:
2、阈值类型
1、QPS
对/testA请求新增流控规则
只允许每秒请求数为1,快速点击时,提示被限流
2、并发线程数
设置一个延时3s的方法,改变流控规则为线程数1
@GetMapping("/testB")
public String testB()
{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "------testB";
}
打开两个浏览器窗口,先访问一次,三秒内在第二个窗口再次访问
一个窗口成功,一个窗口则被阻塞,注意:用不同浏览器启动两个窗口测试最为直观
3、流控模式
1、直接
如上已经演示,只对应一个接口做限流
2、关联
新建规则
方法
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return "------testB";
}
效果:资源/testB达到阈值,使管理资源/testA限流,
类似于A是下订单模块,B是支付模块,那么当支付模块 达到阈值时,需要订单模块进行限流,以防微服务崩溃
使用Postman模拟高并发访问testB
新建线程组让20个请求每0.3s访问一次B,开始后手动访问A
阻塞
4、流控效果
1、直接
已经演示
2、Warm up(预热)
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
默认 coldFactor
为 3,即请求 QPS 从 threshold(阈值) / 3
开始,经预热时长逐渐升至设定的 QPS 阈值。
如图设置
即阈值为10,从10/3=3开始,在5s内增长至10
现象:
设置完成后不停刷新页面发送请求,最开始的5s内,会出现阻塞提示,但是5s后,无一次阻塞(测试时每秒点击不超10次)
3、排队等待
匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。
设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。
修改业务类
@GetMapping("/testB")
public String testB()
{
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
log.info(Thread.currentThread().getName()+"--->testB....");
return "------testB";
}
postman发送请求测试:发送5个请求,每0.3秒发送一次
结果
控制台每秒输出一次,并非0.3秒输出一次,可见请求排队输出
六、Sentinel熔断降级
注意:针对1.8.0及以上版本测试,1.8.0 版本对熔断降级特性进行了全新的改进升级
1、概述
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
2、熔断策略
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 - 异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 - 异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
3、熔断降级规则说明
4、测试
利用工具进行测试
1、慢调用比例
5秒钟内若有5个请求数,且响应时间大于200ms的请求占0.2即熔断10s,10s后进入Half Open状态,若下一个请求正确,则恢复微服务,否则继续熔断
2、异常比例
同慢调用比例
3、异常数
同上
七、Sentinel热点Key
1、概念
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
2、使用
业务类:
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException exception)
{
return "-----dealHandler_testHotKey哭了";
}
@SentinelResource注解
value唯一标识接口
blockHanler用于标记阻塞处理方法,如果不写此方法,则会报错误页面而不是我们可以自定义的友好提示
对于
@SentinelResource
注解方式定义的资源,可以用于热点规则判断
3、测试
设置热点key规则
对索引为1的参数进行限流,(即方法中的p1),当每秒多次点击,触发了阻塞处理方法
4、热点Key高级设置--参数例外项
在高级设置中如下设置,代表如果下标 为0的参数的值为String类型的root时,阈值为500
经过测试,连续点击刷新也不会触发阻塞
八、Sentinel系统规则(了解)
系统规则如下
入口 QPS
即统计所有的接口入口阈值
九、Sentinel Resource配置
Sentinel 提供了 @SentinelResource
注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException
等。使用 Sentinel Annotation AspectJ Extension 的时候需要引入以下依赖
1、按照资源名限流
在8401加入以下依赖
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
controller
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.R;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @auther zzyy
* @create 2020-02-13 16:50
*/
@RestController
public class RateLimitController
{
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public R byResource()
{
return new R(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
public R handleException(BlockException exception)
{
return new R(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
正常测试
配置规则
2、按Url地址限流
新加入方法
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}
注意,此时value属性值不等同于getmapping中的值
测试:
3、自定义限流处理逻辑
问题
1、 创建类处理限流逻辑
BlockException类用于处理异常
注意处理的方法要静态声明,
package com.atguigu.springcloud.handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.R;
/**
* @auther zzyy
* @create 2019-12-10 13:01
*/
public class CustomerBlockHandler
{
public static R handleException(BlockException exception){
return new R(444,"自定义的限流处理信息......CustomerBlockHandler");
}
public static R handleException2(BlockException exception){
return new R(444,"自定义的限流处理信息......CustomerBlockHandler2");
}
}
Controller
/**
* 自定义通用的限流处理逻辑,
blockHandlerClass = CustomerBlockHandler.class
blockHandler = handleException2
上述配置:找CustomerBlockHandler类里的handleException2方法进行兜底处理
*/
/**
* 自定义通用的限流处理逻辑
*/
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public R customerBlockHandler()
{
return new R(200,"按客户自定义限流处理逻辑");
}
2、测试
正常调用
设置限流
结果
处理阻塞的方法走到了我们定义的处理类的指定方法中(限流方法尽量通过名称配置)
4、更多@SentinelResource注解属性说明
十、Sentinel服务熔断+Ribbon+fallback+blockHandler
1、模块搭建
1、 服务提供者9003、 9004
pom、yml、主启动类
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
server:
port: 9004
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9004
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9004.class, args);
}
}
业务类
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.entities.R; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @RestController public class PaymentController { @Value("${server.port}") private String serverPort; public static HashMap<Long,Payment> hashMap = new HashMap<>(); static { hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181")); hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182")); hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183")); } @GetMapping(value = "/paymentSQL/{id}") public R<Payment> paymentSQL(@PathVariable("id") Long id) { Payment payment = hashMap.get(id); R<Payment> result = new R(200,"from mysql,serverPort: "+serverPort,payment); return result; } }
这里用hashmap代替数据库,模拟三条数据
2、服务消费者84
pom,yml
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
配置类
package com.atguigu.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
Controller
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String INVOKE_URL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public R<Payment> fallback(@PathVariable Long id){
R<Payment> res = restTemplate.getForObject(INVOKE_URL + "/paymentSQL/"+id,R.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (res.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return res;
}
}
测试:
9004,9003以轮循方式提供服务
2、fallback
fallback处理程序的运行时异常
加入fallback
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String INVOKE_URL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
public R<Payment> fallback(@PathVariable Long id){
R<Payment> res = restTemplate.getForObject(INVOKE_URL + "/paymentSQL/"+id,R.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (res.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return res;
}
public R handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new R<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
}
测试
程序执行了fallback的方法,而不是错误页面
3、blockHandler
blockHandler只负责sentinel控制台的异常
Controller
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String INVOKE_URL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流
public R<Payment> fallback(@PathVariable Long id){
R<Payment> res = restTemplate.getForObject(INVOKE_URL + "/paymentSQL/"+id,R.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (res.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return res;
}
// public R handlerFallback(@PathVariable Long id,Throwable e) {
// Payment payment = new Payment(id,"null");
// return new R<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
// }
public R blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new R<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
sentinel设置
2s内如果达到三个请求中两个报错,则熔断2s
测试:
首次访问仍然报异常页面
一旦快速刷新,进入如下页面
4、同时配置fallback和blockHandler
访问报异常的数据,最开始符合规则会进入fallback,但如果达到限流规则,仍会进入blockHandler
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String INVOKE_URL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
public R<Payment> fallback(@PathVariable Long id){
R<Payment> res = restTemplate.getForObject(INVOKE_URL + "/paymentSQL/"+id,R.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (res.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return res;
}
public R handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new R<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
public R blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new R<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
对方法做一个限流
测试
访问正确数据过快会进入blockHandler
访问报异常的数据,最开始符合规则会进入fallback,但如果达到限流规则,仍会进入blockHandler
4、exceptionsToIgnore (忽略异常)
此属性会忽略指定类型异常,不经过fallback和blockHandler指定的处理方法
Controller
忽略输入4造成的非法参数异常
package com.atguigu.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class CircleBreakerController {
@Value("${service-url.nacos-user-service}")
private String INVOKE_URL;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流
// @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class})
public R<Payment> fallback(@PathVariable Long id){
R<Payment> res = restTemplate.getForObject(INVOKE_URL + "/paymentSQL/"+id,R.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (res.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return res;
}
public R handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new R<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
public R blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new R<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
测试
没有受到任何拦截而直接抛出错误页面
十一、 Sentinel服务熔断+OpenFeign
1、构建模块
1、修改84模块
加入OpenFeign依赖
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
yml
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
主启动类加上@EnableFeignClients注解激活OpenFeign
service代码
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import com.atguigu.springcloud.service.impl.PaymentFallbackService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public R<Payment> paymentSQL(@PathVariable("id") Long id);
}
实现类
package com.atguigu.springcloud.service.impl;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.entities.R;
import com.atguigu.springcloud.service.PaymentService;
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public R<Payment> paymentSQL(Long id) {
return new R<>(444,"错误!------PaymentFallbackService",new Payment(id,"no this serial!"));
}
}
Controller
/****OpenFeign*******/
@Resource
private PaymentService paymentService;
@GetMapping("/consumer/paymentSQL/{id}")
public R<Payment> paymentSQL(@PathVariable Long id){
return paymentService.paymentSQL(id);
}
2、测试
可以调用,此时再次调用,该调用9003,提前关闭9003
执行了fallback,服务降级返回
2、熔断框架比较
十二、Sentinel的规则持久化
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台
的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
1、加入依赖
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2、yml
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3、Nacos新建配置
内容
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
解释:
发布
启动8401查看
流控规则自动加载并且生效
十三、SpringCloud Alibaba Seata处理分布式事务
单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,
业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
1、Seata术语
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
处理过程
2、下载安装
本次使用1.4.0版本,mysql8.0.23
下载后解压,目录如下
1、修改配置
进入conf文件夹,备份如下两个文件
修改file.conf
配置数据库
修改registry.conf文件
将seata注册进nacos
2、启动测试
先启动nacos,再启动seata
seata没有出现异常,成功
2、搭建测试用数据库
建表文件:
readme文件获取github上的建表脚本
3、 订单/库存/账户业务微服务准备
@GlobalTransactional注解加在Controller上可以控制数据库的事务,例如下单过程中抛出异常,回回滚数据的事务
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order)
{
。。。。。。
}
name是事务组名,rollbackFor是异常类型