Sentinel的初步学习(1)https://blog.csdn.net/BinXNoob/article/details/106432595
系统规则
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
设置系统规则
规定该端口下的所有请求
1秒钟只允许1个资源数通过
这时后,无论你哪个资源请求
再1秒钟的访问量超过阈值,则降级
我们给个资源请求,令sentinel控制台获取他的请求
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "HandlerException")
public CommonResult byResource(){
return new CommonResult(200,"按资源限流进行测试",new Payment(2020L,"serial2020"));
}
public CommonResult HandlerException(BlockException exception){
return new CommonResult(400,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
这个时候我们按照资源名称在sentinel控制台配置上流量限制
这种情况下,1秒钟限制我们只能一次访问,超过就限流
我们可以发现
com.alibaba.csp.sentinel.slots.block.flow.FlowException
这是因为我们得BlockException由Alibaba自带得FlowException限流来处理(及时包圆,源码中的一个方法)
当我们关闭服务后,在去访问sentinel控制台,会发现得我们的资源名,流控规则消失
/*
* 这是对按 URL 地址限流
* 在控制台配置的是GetMapping中得value
* */
@GetMapping("/url")
@SentinelResource("url")
public CommonResult byUrl(){
return new CommonResult(200,"这是对于URL进行资源限流控制测试",new Payment(2020L,"serial2020"));
}
在这里要和资源名限流分开来
url地址限流是对于 @GetMapping("/url")进行配置,在控制台得资源名指的是@GetMapping("/url")
当你超过阈值后,
我们发现我们给我们得URL方法配上一个兜底方法后,超阈值访问后,还是不会返回自己的方法,而是系统默认的
写路径情况, 不走自定义的兜底方法。
全局兜底方法
public class MyHandler {
public static CommonResult handlerException(BlockException e){
return new CommonResult(444,"按照客户的自定义全局配置Exception==================1");
}
public static CommonResult handlerException2(BlockException e){
return new CommonResult(444,"按照客户的自定义全局配置Exception==================2");
}
}
设置一个请求配置
/*
* 这是用于配置全局自定义异常处理信息
* blockHandlerClass 指定我们的异常信息类
* blockHandler 指定异常信息类中的异常信息处理方法
* */
@GetMapping("/consumerHandler")
@SentinelResource(value = "consumerHandler",
blockHandlerClass = MyHandler.class,
blockHandler = "handlerException2")
public CommonResult consumerHandeler(){
return new CommonResult(200,"这是对于用户自定义配置的正常访问请求",new Payment(2020L,"serial2020"));
}
详情见官网:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
这个注解方式埋点不支持private方法
注意:注解方式埋点不支持 private 方法。
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource
注解包含以下属性:
value
:资源名称,必需项(不能为空)
entryType
:entry 类型,可选项(默认为 EntryType.OUT
)
blockHandler
/ blockHandlerClass
: blockHandler
对应处理 BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是 public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback
:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
Throwable
类型的参数用于接收对应的异常。fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。defaultFallback
(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了
exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
Throwable
类型的参数用于接收对应的异常。fallbackClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException
)。
我们建立我们的84端口得controller层
@RestController
public class OrderController {
@Resource
private RestTemplate restTemplate;
@Value("${server.port}")
private String serverPort;
@Value("${service-url.nacos-user-service}")
private String serviceURL;
@GetMapping("/consumer/fallback/{id}")
public CommonResult<Payment> fallback(@PathVariable("id")Long id){
CommonResult<Payment> result = restTemplate.getForObject(serviceURL+"/paymentSQL/"+id,CommonResult.class);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException============,非法参数异常");
}else if(result.getData() == null){
throw new NullPointerException("NullPointerException====================,空指针异常");
}
return result;
}
@SentinelResource(value = "fallback") // 没有配置任何兜底方法------直接报错去到前端得500页面
这个时候,我们访问/consumer/fallback/4(前提,数据库中id=4,没有数据)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0SqEMJ43-1590758140860)(D:\ssm\ssm学习\typora-user-images\1588389534(1)].png)
@SentinelResource(value = "fallback") // 没有配置任何兜底方法------直接报错去到前端得500页面
/*
* 返回fallback
**/
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"这是我们的自定义兜底方法==========="+e.getMessage(),payment);
}
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责sentinel控制台的违规报错
/*
* handlerFallback
* */
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult(445,"blockHandler-sentinel 限流,无此流水"+e.getMessage(),payment);
}
当你访问一个错误连接时,没达到降级的要求时
他由于我们没有配置fallback方法
直接给我们调回一个错误页面
返回自定义的兜底方法
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")// 开启了我们的业务层请求出错和sentinel的违规报错
public CommonResult<Payment> fallback(@PathVariable("id")Long id){
CommonResult<Payment> result = restTemplate.getForObject(serviceURL+"/paymentSQL/"+id,CommonResult.class);
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException============,非法参数异常");
}else if(result.getData() == null){
throw new NullPointerException("NullPointerException====================,空指针异常");
}
return result;
}
/*
* 返回fallback
**/
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"这是我们的自定义兜底方法==========="+e.getMessage(),payment);
}
/*
* handlerFallback
* */
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id,"null");
return new CommonResult(445,"blockHandler-sentinel 限流,无此流水"+e.getMessage(),payment);
}
直接返回我们的自定义sentinel控制台方法
也返回我们的自当以兜底方法
这是为什么?
先进入了sentinel控制台的报错
因为这是Sentinel异常,因为Sentinel是控制请求的,请求还没到业务内之前就被Sentinel给拦了
exceptionsToIgnore = {IllegalArgumentException.class}
在SentinelResource中有这个属性,这个属性用来排除异常
这里我们排除了id=4的异常
if(id == 4){
throw new IllegalArgumentException("IllegalArgumentException============,非法参数异常");
所以,当我们运行起来后,访问id=4的请求页面时,会去到默认的前端500页面
OpenFegin=======也就是service完成一些任务集成,controller只完成简单业务访问
修改我们的84端口
POM文件添加feign 的依赖
yml要激活sentinel对于feign的支持
# 激活sentinel对feign的支持
feign:
sentinel:
enabled: true
org.springframework.cloud
spring-cloud-starter-openfeign
添加我们的service业务层
service接口
@FeignClient(value = "provider-payment-service",fallback = PaymentServiceImpl.class)
public interface PaymentService {
@GetMapping("/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
serviceImpl
@Component
public class PaymentServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(456,"这是我们的自定义兜底方法=======PaymentServiceImpl"+id,new Payment());
}
}
修改我们的controller
@RestControllerpublic class FeignController {
@Resource
private PaymentService paymentService;
@GetMapping("/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
return paymentService.paymentSQL(id);
}
}
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于时间响应、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件形式 | 接口形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于QPS、支持基于调用关系的限流 | 有限的支持 | Rate Limiter |