1.引言
微服务体系架构下,服务容错是一个大事,常见的容错方案有
- 超时
- 流控(限流)
- 熔断降级
在业界也有很多可选择的容错产品,比如说服务之间调用ribbon+resttemplate支持超时设置,feign支持超时,甚至我们在设计实现api的时候,也会考虑相关的api超时机制。
比如说支持流控、熔断降级的产品hystrix、resilience4j,以及阿里开源的sentinel。
当然今天我们的主角是sentinel,因为它的优秀,我准备写一个短系列文章分享介绍使用它!整理了一下,内容大致包括
- 入门使用
- 控制台dashboard
- spring cloud alibba 整合sentinel使用
- 扩展sentinel之错误页面
- 扩展sentinel之区分来源
- 扩展sentinel之支持restful url
- 扩展sentinel之规则持久化
本篇文章是第一篇,我们从入门开始!
2.sentinel简介
关于sentinel介绍,我想推荐你去看官网,非常详细!这也是我想推荐给小伙伴们学习新东西的一个方式
- 一定要看官网,一定要掌握学习的方法,总结形成自己的知识体系!
- 反之,如果我们只是满足于借助搜索引擎翻几篇文章,比如说吧就看我的博客,那是远远不够的!
- 借助搜索引擎,看看博客,只能是快速入门!所以还是那句话:一定要看官网
那么官网的地址在哪呢?github,我贴一下它的地址:
顺便截个图:
总结一下,官方的介绍
- sentinel是一个服务稳定性的防护组件,流控、熔断降级规则丰富,应用场景丰富
- 还提供的dashboard控制面板,相关规则管理,比如说规则动态配置刷新;支持监控
- 更关键的是sentinel不依赖任何第三方库,或者开源组件,这就很感人了!不管是普通sevlet web应用,老一代spring web应用,还是springboot应用,还是整合到spring cloud应用,统统都满足!
3.入门案例
3.1.搭建环境
我这里以springboot应用为例,毕竟springboot今天是主流应用基础框架。首先我们需要引入sentinel依赖
xml
<!--公共变量定义-->
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
</properties>
<!--sentinel依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.2</version>
</dependency>
这里关于版本的选择,我贴一个官方的地址:github.com/alibaba/spr...
因为我这里选择的springboot版本是:2.3.2.RELEASE,所以sentinel版本参照官网选择的版本是:1.8.2
3.2.加载规则
对于sentinel防护组件,使用的关键是两个点
- 声明资源
- 定义相关规则,保护资源
3.2.1.声明资源
java
/**
* <p>
* sentinel 资源
* </p>
*
* @author [email protected]
*/
public interface MySentinelResource {
/**
* 流控资源
*/
public static final String FLOW_RESOURCE = "flowResource";
}
3.2.2.加载资源
java
/**
* <p>
* 加载sentinel规则
* 1.流控规则
* 2.降级规则
* 3.系统规则
* 4.热点规则
* 5.授权规则
* </p>
*
* @author [email protected]
*/
@Component
@Slf4j
public class LoadSentinelRules {
public LoadSentinelRules(){
// 初始化加载流控规则
initFlowRules();
log.info("加载了流控规则.");
}
/**
* 初始化流控规则
*/
public void initFlowRules(){
// 流控规则集合
List<FlowRule> flowRules = Lists.newArrayList();
FlowRule rule = new FlowRule();
rule.setResource(MySentinelResource.FLOW_RESOURCE);// 资源
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 流控阈值类型:qps
rule.setCount(1);// 流控阈值:1
flowRules.add(rule);
FlowRuleManager.loadRules(flowRules);
}
}
3.3.流控规则
编写一个conttroller,将相关的端点作为资源,通过sentinel进行保护
java
/**
* <p>
* sentinel demo controller
* </p>
*
* @author [email protected]
*/
@RestController
@RequestMapping("sentinel")
@Slf4j
public class SentinelController {
@RequestMapping("flow")
public String flow(String userId){
Entry entry = null;
String result = "ok!";
try{
// 1.开始进入资源,流控保护开始
SphU.entry(MySentinelResource.FLOW_RESOURCE);
// 2.被保护的资源
log.info("SentinelController--flow,param:{}", userId);
}catch (BlockException e){
log.error("发生流控异常! msg:{}", e);
result = "error!限流了!";
}finally {
// 3.释放资源,需要与SphU.entry配对
if(entry != null){
entry.exit();
}
}
return result;
}
}
关键代码说明
- 第一步,通过SphU.entry方法声明进入资源
- 第二步,编写需要被保护资源
- 第三步,通过 entry.exit释放资源
3.4.流控效果
把应用启动起来,我们上面配置一个流控规则,表示每秒中允许通过的qps是1,超过1即需要流控(限流)
请求api:http://127.0.0.1:8080/sentinel/flow?userId=1,1秒内多次刷新
3.5.熔断降级规则
通过上面的案例,我们看到了sentinel流控的效果。不过实现的代码,看起来比较糟糕!你还记得这段代码吗?
java
@RequestMapping("flow")
public String flow(String userId){
Entry entry = null;
String result = "ok!";
try{
// 1.开始进入资源,流控保护开始
SphU.entry(MySentinelResource.FLOW_RESOURCE);
// 2.被保护的资源
log.info("SentinelController--flow,param:{}", userId);
}catch (BlockException e){
log.error("发生流控异常! msg:{}", e);
result = "error!限流了!";
}finally {
// 3.释放资源,需要与SphU.entry配对
if(entry != null){
entry.exit();
}
}
return result;
}
需要通过
- try{...}catch(BlockException e){...}finally{...}代码块
- SphU.entry声明资源开始
- 通过entry.exit声明保护资源结束
实际项目中,如果每个需要作为资源被保护起来的业务方法,都这么去写,相信小伙伴们一定很抓狂!
那么有没有什么更好的实现方法呢?答案是有,sentinel给我们提供了一个 @SentinelResource注解,只需要在业务方法上加上该注解,那么sentinel就会自动把该业务方法作为资源保护起来。
正好,通过熔断降级规则案例,来结合@SentinelResource注解一起使用。
3.5.1.引入依赖
要使用@SentinelResource方式保护资源,需要我们引入一个新的依赖,本质上注解方式底层实现是AOP
xml
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.2</version>
</dependency>
3.5.2.sentinel aop配置
java
/**
* <p>
* sentinel aop 配置
* </p>
*
* @author [email protected]
*/
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
3.5.3.加载降级规则
java
/**
* 初始化降级规则
*/
public void initDegradeRules(){
List<DegradeRule> degradeRules = Lists.newArrayList();
DegradeRule rule = new DegradeRule();
rule.setResource(MySentinelResource.DEGRADE_RESOURCE);// 资源
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);// 阈值类型:慢调用比例
rule.setMinRequestAmount(5);// 最小请求数
rule.setTimeWindow(1);// 熔断时长,单位秒
rule.setSlowRatioThreshold(1);// 比例阈值
rule.setCount(1);// 最大RT 单位 毫秒
rule.setStatIntervalMs(1000);// 统计时长,单位毫秒
degradeRules.add(rule);
DegradeRuleManager.loadRules(degradeRules);
}
3.5.4.编写资源
java
/**
* 测试熔断降级规则
* @return
*/
@RequestMapping("degrade")
@SentinelResource(value =MySentinelResource.DEGRADE_RESOURCE, fallback = "degradeFallback")
public String degrade(){
log.info("SentinelController--annotationDegrade start");
int time = new Random().nextInt(1000);
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("SentinelController--annotationDegrade end,耗时:{}毫秒",time);
return "degrade ok!";
}
// 降级方法
public String degradeFallback(Throwable e){
log.error("熔断降级了,异常消息:{}", e);
return "error!熔断降级了!";
}
3.5.5.降级效果
请求:http://127.0.0.1:8080/sentinel/degrade?userId=1,且不断刷新
3.5.6. @SentinelResource注解说明
到此,分别通过SphU.entry,与注解@SentinelResource,实现了sentinel流控、与熔断降级的效果。我们发现@SentinelResource的方式真是好啊!代码优雅了很多!
那么对于该注解,做一个简单的说明,它的关键属性有
- value:指定资源名称
- blockHandler:如果是流控,用于指定发生流控后的处理方法
- fallback:如果是降级,用于指定发生降级后的处理方法
这三个是比较常用的属性,更多属性,推荐你去看官方文档,连接地址:github.com/alibaba/Sen...
本文源码地址:gitee.com/yanghouhua/...