一、熔断降级
熔断降级是一种基于消费者报复自身的手段,在调用提供方的接口时,基于慢调用比例、异常比例、异常数进行熔断降级。
1. 熔断策略
1.1 慢调用比例
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
示例代码:
@GetMapping("/test")
public String test() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
return "服务降级";
}
如上配置:代表在3秒内请求次数超过2次,且RT大于1秒的比例大于0.5就会进入熔断,熔断后会持续10秒。10秒后会进入半开状态,如果这时候过来的请求如果依旧是大于1秒的慢请求就继续熔断10秒,再进入半开状态。如果这时候过来的不是大于1秒的慢请求就恢复正常。
1.2 异常比例
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
示例代码:
@GetMapping("/test2")
public String test2() {
int a = 1 / 0;
return "服务降级";
}
如上配置:代表在1秒内请求次数超过10次,且异常比例大于0.5就会进入熔断,熔断后会持续10秒。10秒后会进入半开状态,如果这时候过来的请求依旧异常就继续熔断10秒,再进入半开状态。如果这时候过来的请求没有异常就恢复正常。
1.3 异常数
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。
示例代码:
@GetMapping("/err")
public String err() {
int a = 1 / 0;
return "服务降级";
}
如上配置:代表在1秒内请求次数超过4次,且异常数超2个就会进入熔断,熔断后会持续10秒。10秒后会进入半开状态,如果这时候过来的请求依旧异常就继续熔断10秒,再进入半开状态。如果这时候过来的请求没有异常就恢复正常。
2. 整合OpenFeign降级
2.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2 添加bootstrap.yml
原来我们使用的是feign.hystrix.enabled=true 来开启feign调用的熔断降级。现在使用的是sentinel
feign:
sentinel:
enabled: true
2.3 在程序中使用配置
新建一个类,实现feign接口,将新建的对象注入到上下文中。
@Component
public class StockFeignFallback implements StockFeign {
@Override
public String reduce(String id) {
return "服务降级了。。。。";
}
}
在Feign上添加fallback就行了。
@FeignClient(name = "stock-service", path = "/stock",fallback = StockFeignFallback.class)
public interface StockFeign {
@GetMapping("/reduce/{id}")
String reduce(@PathVariable("id") String id);
}
这样OpenFeign的降级就配置完成了。
二、热点参数限流
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的数据,并对其访问进行限制。
如下,我们存在一个根据id获取商品的接口,其中id为2的是热点数据,其他的为普通数据,现在我们要这个接口对获取普通数据没有影响,热点数据进行限制。
注意:
- 热点参数限流必须使用
@SentinelResource 注解,所以必须自己写一个blockHandler,而不能使用全局异常配置。 - 参数类型必须是7中基本类型才可以生效。
@GetMapping("/getById/{id}")
@SentinelResource(value = "getById", blockHandler = "getByIdBlockHandle")
public String getById(@PathVariable("id") Integer id) {
log.info("获取商品!{}", id);
return "获取商品成功.";
}
public String getByIdBlockHandle(@PathVariable("id") Integer id, BlockException blockException) {
return "获取商品接口热点参数限流";
}
在sentinel的簇点链路也面找到getById 资源进行热点设置,如下表示1秒以内索引为0的参数访问超过300就进行热点参数限流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
设置完成后点击编辑,这时候会出现高级选项。进行高级选项配置。针对索引为0的参数,值为2的参数限流阈值为50。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
三、系统规则
在我们真正的微服务中,可能并没有让我们能找到流量访问的规律,但是这时我们还是需要保证系统的高可用,这种情况我们需要对整个系统配置一个兜底的方案。
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
- CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
我们可以在系统规则上新增系统规则,可以按照一下几个维度进行规则设置。到时候达到这个设定值就会触发整个系统的降级。
四、授权控制规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
来源访问控制规则(AuthorityRule )非常简单,主要有以下配置项:
resource :资源名,即限流规则的作用对象。limitApp :对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB。strategy :限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式。
如下图所示:
这代表我们对/stock/reduce/{id}进行了授权规则配置,只允许order-service进行访问。我们只需要在stock-service服务实现RequestOriginParser 接口
@Component
@Slf4j
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String serverName = httpServletRequest.getHeader("serviceName");
log.info("服务名称==>>:{}", serverName);
return serverName;
}
}
在我们调用方的话有很多地方可以在header上添加serviceName
feign 调用中,我们可以实现RequestInterceptor ,在请求中添加请求头
@Component
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {
@Resource
private Environment environment;
@Override
public void apply(RequestTemplate requestTemplate) {
String access_token = UUID.randomUUID().toString();
requestTemplate.header("Authorization", access_token);
requestTemplate.header("serviceName", environment.getProperty("spring.application.name"));
}
}
如果是通过gateway路由请求的话也可以在gateway中进行如下配置
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=serviceName,gateway
--------------最后感谢大家的阅读,愿大家技术越来越流弊!--------------
--------------也希望大家给我点支持,谢谢各位大佬了!!!--------------
|