持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
一、Sentinel 是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
1-1、Sentinel的特征:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
1-2、Sentinel和Hystrix对比
二、Sentinel快速开始
在官方文档中,定义的Sentinel进行资源保护的几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
Entry entry = null;
// 务必保证 finally 会被执行
try {
// 资源名可使用任意有业务语义的字符串 开启资源的保护
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑 method
// do something...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级 Sentinel定义异常 流控规则,降级规则,热点参数规则。。。。 服务降级(降级规则)
// 进行相应的处理操作
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常 RuntimeException 服务降级 mock feign:fallback
Tracer.traceEntry(ex, entry);
} finally{
// 务必保证 exit,务必保证每个 entry 与 exit 配对
if(entry!=null){
entry.exit();
}
}
2-1、Sentinel资源保护的方式
2-1-1、API实现
首先创建一个springboot项目,可以不依赖之前的父项目,sentinel不依赖spring Cloud Alibaba,可以单独进行使用,可以单独放在分布式服务里面使用。
2-1-1-1、引入依赖
<!--web 场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--导入sentinel依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2-1-1-2、编写测试逻辑
package com.jony.sentinel.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@RestController
@Slf4j
public class TestController {
private static final String RESOURCE_NAME = "hello";
@RequestMapping(value = "/hello")
public String hello() {
Entry entry = null;
try {
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
String str = "hello world";
log.info("====="+str);
return str;
} catch (BlockException e1) {
// 资源访问阻止,被限流或被降级
//进行相应的处理操作
log.info("block!");
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
} finally {
if (entry != null) {
entry.exit();
}
}
return null;
}
/**
* 定义流控规则
*/
//通过添加@PostConstruct注解,设置在当前Bean在初始化的时候,先加载这个方法
@PostConstruct
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// Set limit QPS to 20.
rule.setCount(1);
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
方法说明:hello是对外暴露的接口,initFlowRules为初始化Sentinel规则的方法。
1、在initFlowRules方法中,设置了相关属性,目前方法中设置了每秒QPS为1,具体属性说明如下:
2、在hello方法中,设置了资源名称,这样sentinel控制台中就可以看到对应的资源监控信息。
测试效果:
每秒刷新一下页面的情况下是可以正常访问的
刷新频繁的情况下,就会发生熔断的触发,如下:
到此sentinel的简答使用就结束了,后面再介绍在Spring Cloud Alibaba中的使用
2-1-1-3、缺点
- 业务侵入性很强,需要在controller中写入非业务代码.
- 配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则
2-1-2、@SentinelResource注解的使用
通过上面的代码虽然可以实现sentinel,但由于对代码的侵入性太强,如果是这样肯定会被抛弃,因此同时使用@SentinelResource就可以实现,下面来看一下是如何实现的
2-1-2-1、引入依赖
<!--使用@SentinelResource 注解的依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.0</version>
</dependency>
通过上面的依赖代码,可以看到其是依赖切面来实现的。
2-1-2-2、配置切面支持
2-1-2-3、@SentinelResource的使用
可以看下github上面的说明:github.com/alibaba/Sen…
2-1-2-3-1、给接口添加@SentinelResource注解
2-1-2-3-2、添加流控处理方法
2-1-2-3-2-3、添加流控规则
流控规则,在github说明中,提供了如下属性
2-1-2-3-2-4、测试
未被流控的处理结果:
被流控处理的结果:
2-1-2-3-2-5、特别说明
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。
2-1-2-3-3、添加降级规则
如下,为熔断降级规则相关参数
2-1-2-3-3-1、熔断规则
/**
* 设置降级规则
*/
@PostConstruct
private static void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource("editUser");
//设置熔断策略,0:调用比例 1:异常比例 2:异常数策略
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
//慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用)
rule.setCount(2);
//触发熔断的最小请求数
rule.setMinRequestAmount(2);
//统计时长:一分钟内,执行了2此请求,并且2次异常就触发熔断
rule.setStatIntervalMs(60*1000);
//熔断时长,单位为 s
//一旦触发熔断,再次请求对应的接口,如果第一次就发生异常就会直接调用降级
//熔断时长过去之后,就会进入一个半开状态,也就是恢复接口的请求调用,
// 如果第一次还是异常,就再次降级,上面设定的条件就不起作用了
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
2-1-2-3-3-2、添加请求方法
因为使用了blockHandlerForGetUser方法,因此需要注意的是,当前方法的参数,需要和blockHandlerForGetUser的参数一致,blockHandlerForGetUser方法可以额外添加BlockException参数。
2-1-2-3-3-3、测试
访问接口,前三次都是如下错误信息:
第四次接口实现降级处理:
第五次显示异常信息:
第六次显示降级处理:
通过以上就证明了上面的结论:如果接口触发降级规则,在下次半开状态请求,如果第一次就触发异常,就会再次降级,之前设置的降级规则就不会起作用。
2-1-2-4、总结
通过以上处理,我们的代码就不必过于侵入业务代码,只需要添加@SentinelResource注解,就可以进行处理。
另外流控规则一般设置在服务提供方;降级规则一般设置在服务的消费端。