官网
gibhub降级
平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级窗口期过后关闭断路器
异常比列(秒级)
QPS >=5且比例(秒级统计)超过阈值时,触发降级,时间窗口结束后,关闭降级
Sentinel的断路器是没有半开状态的
Sentinel熔断隆级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高) ,对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体参考Hystrix
@GetMapping("/testD")
public String testD() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("testD 测试RT");
return "----testD";
}
用Jmeter压测
然后浏览器http://127.0.0.1:8401/testD发现无法访问了
代码
永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了
后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK
介绍
异常比例( DEGRADE_GRADE_EXCEPTION_RATIO ):当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule中的timewindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0%- 100%。
测试
@GetMapping("/testD")
public String testD()
{
log.info("testD 测试RT");
int age = 10/0;
return "------testD";
}
异常数( DEGRADF_GRADE_EXCEPTION_cOuNT ):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timewindow小于60s,则结束熔断状态后仍可能再进入熔断状态。时间窗口一定要大于60s
测试
controller控制层代码
@GetMapping("/testE")
public String testE()
{
log.info("testE 测试异常数");
int age = 10/0;
return "------testE 测试异常数";
}
浏览器http://localhost:8401/testE
http://localhost:8401/testE,第一次访问绝对报错,因为除数不能为零,
我们看到error窗口,但是达到5次报错后,进入熔断后降级。
兜底方法分两种
之前的例子,限流出问题后,都是用sentinel系统默认的提示: Blocked by Sentinel (flow limiting)
我们能不能自定?类似hystrix,某个方法出问题了,就找对应的兜底降级方法?
从HystrixCommand到SentinelResource
从HystrixCommand到哨兵资源
兜底方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
return "------deal_testHotKey,o(╥﹏╥)o";
}
测试代码
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按照资源名称限流测试", new Payment(2020L, "serial001"));
}
@GetMapping("/byResource/exection")
@SentinelResource(value = "byResourceForExection", blockHandler = "handleException")
public CommonResult byResourceForExection() {
int i = 10 / 0;
return new CommonResult(200, "按照异常数做降级", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
//注意如果使用url进行限流降级那么SentinelResource配置的blockHandler将无效
public CommonResult byUrl() {
return new CommonResult(200, "按照byUrl限流测试", new Payment(2020L, "serial002"));
}
//CustomerBlockHandler
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult customerBlockHandler() {
return new CommonResult(200, "按照客户自定义限流测试", new Payment(2020L, "serial003"));
}
}
配置
访问
注意资源名称要和@SentinelResource()的value属性相同才会限流
包括限流降级等都可以使用 注意 要根据@SentinelResource中的value属性进行配置规则 根据url不会有效果
替换默认显示Blocked by Sentinel (flow limiting)
为我们自定义的
参考上边代码 返回值参数要一致并且在参数中添加BlockException exception
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
/**
* @author 夏天
*/
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(444, "按照客户自定义的Glogal 全局异常处理 ---- 1", new Payment(2020L, "serial003"));
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(444, "按照客户自定义的Glogal 全局异常处理 ---- 2", new Payment(2020L, "serial003"));
}
}
使用处理类
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handlerException2")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}
测试
http://127.0.0.1:8401/rateLimit/customerBlockHandler
当点击过快时候触发限制
你知道Sentinel限流、降级的统一处理吗?
# 只包含主要配置其余省略
spring:
cloud:
sentinel:
servlet:
blockPage: https://www.baidu.com/ #配置统一处理页面
官方介绍:github热点key限流
资源名要对应 @SentinelResource中的value才会有效果
浏览器继续使用@getMapper中的路径进行访问 http://127.0.0.1:8401/testHotKey?p1=1
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKeyB", blockHandler = "deal_testHotKey")
//value 唯一即可 规范一般为方法名 blockHandler如果不配置页面直接抛出信息
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "----testHotKey";
}
//返回值 请求参数一致 并且添加上 BlockException exception 异常
public String deal_testHotKey(String p1, String p2, BlockException exception) {
return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
}
测试
http://127.0.0.1:8401/testHotKey?p1=1
根据配置1秒中的QPS的阈值为1次 所以点击过快会出现错误提示,这个提示是通过@SentinelResource注解自定义的
注意
资源名要对应 @SentinelResource中的value才会有效果 请注意看边的配置
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
需求
我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
假如当p1的值等于5时,它的阈值可以达到200
实现
当p1等于5的时候,阈值变为200
当p1不等于5的时候,阈值就是平常的1
前提条件:热点参数的注意点,参数必须是基本类型或者String
手贱添加异常看看…
@sentinelResource
处理的是sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException
int age = 10/0,这个是java运行时报出的运行时异常RunTimeException(其他异常也不管),@SentinelResource不管
总结
@sentinelResource主管配置出错,运行出错该走异常走异常
先看看官网介绍:gibhub系统自适应限流
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。
接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。
注:这种系统自适应算法对于低 load 的请求,它的效果是一个“兜底”的角色。对于不是应用本身造成的 load 高的情况(如其它进程导致的不稳定的情况),效果不明显。