一、为什么使用断路器
在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册
与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式
执行,这样就有可能因为网络原因或是依赖服务自身间题出现调用故障或延迟,而这些问
题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因
等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。为了解决这样的问题, 产生了断路器等一系列的服务保护机制。
二、断路器两大常用组件Sentinel和Hystrix对比
| Sentinel | Hystrix |
---|
隔离策略 | 信号量隔离(并发控制) | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于慢调用比例、异常比例、异常数 | 基于异常比例 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 RxJava) |
动态规则配置 | 支持近十种动态数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
单机限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 |
集群流控 | 支持 | 不支持 |
流量整形 | 支持预热模式与匀速排队控制效果 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
热点识别/防护 | 支持 | 不支持 |
多语言支持 | Java/Go/C++ | Java |
Service Mesh 支持 | 支持 Envoy/Istio | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、实时监控、机器发现等 | 简单的监控查看 |
维护 | 持续维护 | 停止维护 |
使用方式 | 单独一个组件,可以独立。直接界面化的细粒度统一配置 | 需要程序员手工搭建监控平台 |
三、 sentinel的特性
四、sentinel两个部分
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
五、使用sentinel三步骤
六、使用核心库实现sentinel三步骤
引入核心库jar包 (如果已经引入了spring-cloud-alibaba-dependencies不需要单独选择版本,只需直接引用)
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.1</version>
</dependency>
1、定义资源提供三种方式
- 使用@SentinelResource注解提供使用AspectJ拓展用于自动定义资源
- Sphu(抛出异常)
- Spho(返回布尔类值)
- 使用@SentinelResource
a)引入依赖dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.1</version>
</dependency>
如果使用原生spring需要通过配置将sentinelAspectConfiguration注册为一个springbean。如果使用springcloudalibaba则不需要@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
查看SentinelResourceAspect方法
可以看到SentinelResourceAspect 使用aspect的around拦截,拦截标注有SentinelResource的注解,进入方法之前调用SphU.entry(resourceName, entryType),结束之后调用entry.exit();底层和另外两种方式是一样的
b) 需要定义资源的地方使用@SentinelResource注解并定义一个资源名
@SentinelResource属性值
属性 | 说明 |
---|
value | 资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的。 |
fallback | fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 |
entryType | 流量类型类型,可选项,有IN和OUT两个选项,默认为 EntryType.OUT。 |
blockHandlerClass | blockHandler 函数默认需要和原方法在同一个类中,如果希望使用其他类的函数,则需要指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
blockHandler | blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。 |
fallbackClass | fallbackClass的应用和blockHandlerClass类似,fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
defaultFallback | 如果没有配置defaultFallback方法,默认都会走到这里来。默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。 |
exceptionsToIgnore | 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 |
try (Entry entry = SphU.entry("resourceName")) {
} catch (BlockException ex) {
}
if (SphO.entry("自定义资源名")) {
try {
} finally {
SphO.exit();
}
} else {
}
2、定义规则 (使规则生效可以使用@PostConstruct注解)
参数 | 说明 | 默认值 |
---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,是按照 QPS 还是线程数 | QPS模式 |
limitApp | 是否根据调用者来限流 | 否 |
strategy | 判断的根据是资源自身,还是根据其它资源 (refResource),还是根据链路入口 (refResource) | 根据资源本身 |
controlBehavior | 发生拦截后的流量整形和控制策略(直接拒绝 / 排队等待 / 慢启动模式) | 直接拒绝 |
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(“资源名”);
rule1.setCount(2);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
参数 | 说明 | 默认值 |
---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 降级模式,根据 RT 降级还是根据异常比例降级 | RT |
timeWindow | 降级时间 | |
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(“资源名”);
rule.setCount(10);
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
参数 | 说明 | 默认值 |
---|
resource | 资源名,资源名是限流规则的作用对象 | |
limitApp | 授权使用限制来源方 | default |
count | 限流阈值 | |
grade | 0:线程数(客户端并发控制)1:QPS | QPS |
paramIdx | 必填项,热点参数索引位置 对应SphU.entry(xxx, args)中的参数索引位置 | |
controlBehavior | 0:(直接拒绝)2:匀速通过 | 0 |
maxQueueingTimeMs | 当controlBehavior=2时,排队等待时间 | |
burstCount | 应对突发流量额外允许的数量 | |
durationInSec | 统计窗口时间长度 | 1 |
paramFlowItemList | 额外选项,针对特定的参数单独限流 | |
clusterMode | 是否为集群模式 | false |
clusterConfig | 集群限流配置 | |
private static void initParamFlowRule() {
ParamFlowRule rule = new ParamFlowRule(“资源名”);
rule.setParamIdx(0);
rule.setCount(5);
ParamFlowItem item = new ParamFlowItem();
item.setObject(String.valueOf(10));
item.setClassType(int.class.getName());
item.setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
参数 | 说明 | 默认值 |
---|
highestSystemLoad | 最大的 load1 | -1 (不生效) |
avgRt | 所有入口流量的平均响应时间 | -1 (不生效) |
maxThread | 入口流量的最大并发数 | -1 (不生效) |
private void initSystemProtectionRule() {
List<SystemRule> rules = new ArrayList<>();
SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(10);
rules.add(rule);
SystemRuleManager.loadRules(rules);
}
参数 | 说明 | 默认值 |
---|
resource | 资源名,资源名是限流规则的作用对象 | |
resource | 对应的黑名单/白名单,不同 origin 用逗号分隔 | default,代表不区分调用来源 |
resource | 限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式 | AUTHORITY_WHITE |
public void initAuthorityRule() {
AuthorityRule authorityRule = new AuthorityRule();
authorityRule.setResource("资源名");
authorityRule.setStrategy(RuleConstant.AUTHORITY_WHITE);
authorityRule.setLimitApp("白名单应用");
AuthorityRuleManager.loadRules(Collections.singletonList(authorityRule));
}
3、检验规则是否生效
- 接口
http://IP:对外通信端口/getRules?type={type} - 实际访问接口