微服务系列:Spring Cloud 阿里巴巴哨兵详解入门
- 微服务系列:服务网关 Spring Cloud Gateway 集成 Sentinel 限流
在前面学习 Spring Cloud Gateway
的时候,我们已经使用过了 Sentinel
来进行限流,但是并没有对 Sentinel
进行详细的学习,那今天就要补上这块知识了。
话不多说,开始今天的学习。
Sentinel 介绍
Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
1. Sentinel 基本概念
- 资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
- 规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
2. Sentinel 特征
Sentinel
具有以下特征:
-
丰富的应用场景: Sentinel 承接了阿里巴巴近十年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围),消息削峰填谷,集群流量控制,实时熔断下游不可用应用等
-
完美的实时监控: Sentinel 同事提供实时的监控功能,您可以在控制台看到接入应用的单台机器秒级数据,甚至500台一下规模的集群的汇总运行情况
-
广泛的开源生态: Sentinel 提供开箱即用的与其他框架/库的整合模块,例如与SpringCloud,Dubbo,gRPC的整合,您只需要引入响应的依赖并进行简单的配置即可快速接入Sentinel
-
完美的SPI扩展点: Sentinel 提供简单易用的,完美的SPI扩展接口,可以通过实现扩展接口来快速定制逻辑,例如定制规则管理,适配动态数据源等
3. Sentinel VS Hystrix
Sentinel
和之前常用的熔断降级库 Hystrix 有什么不同呐?
Hystrix
的不足之处:
- 需要自己手工搭建监控平台
- 没有一套 web 界面可以给我们进行更细粒度化的配置流控、速率控制、熔断、降级等
- Hystrix 常用的线程池隔离会造成线程上下切换的 overhead 比较大。
Hystrix 的关注点在于以 隔离 和 熔断 为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。
而 Sentinel 的侧重点在于:
- 多样化的流量控制
- 熔断降级
- 系统负载保护
- 实时监控和控制台
项目集成
Sentinel
可以简单的分为 Sentinel
核心库和 Dashboard控制台
。核心库不依赖 Dashboard
,但是结合 Dashboard
可以取得最好的效果。
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。
上面有说 资源是 Sentinel 的关键概念
使用 Sentinel 来进行资源保护,主要分为这几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
看了上面这么多概念,感觉头都要秃了,我们还是来实战试试吧。
1. 下载启动
我们可以从https://github.com/alibaba/Sentinel/releases
下载sentinel-dashboard-$version.jar
包。
我们可以用以下命令来启动下载的 jar 包:
java -Dserver.port=8718 -jar sentinel-dashboard-1.8.3.jar
其中 -Dserver.port=8718
指定控制台端口
启动成功浏览器访问地址 http://localhost:8718 ,默认用户名密码:sentinel/sentinel
登录进去之后是这样的,空空如也,接下来我们就要看看到底如何使用了。
2. 如何集成
1、添加依赖
<!-- springcloud alibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2、添加 Sentinel
配置
server:
port: 9201
spring:
application:
# 应用名称
name: cloud-sentinel
cloud:
sentinel:
transport:
# 控制台地址
dashboard: 127.0.0.1:8718
3、测试接口
@RestController
public class TestController {
@GetMapping("/testA")
public Object testA(){
return "testA......";
}
@GetMapping("/testB")
public Object testB(){
return "testB......";
}
}
4、启动项目,访问控制台
访问 Sentinel
控制台发现还是空空如也,这是怎么回事呐?
原来Sentinel
控制台有懒加载机制,我们需要访问一次我们服务的接口,控制台才会出现相关信息
我们来访问一下接口 localhost:9201/testA
再次刷新 Sentinel
控制台,发现出现了很多请求相关信息
我们也可以在配置文件中配置 spring.cloud.sentinel.eager: true 属性来取消控制台懒加载
这一步,到这里项目只是成功集成了 Sentinel
并打开了控制台,关于控制台上的相关操作,此文暂不讨论,下篇文章开始学习。
使用案例
上面有说过,使用 Sentinel 来进行资源保护,主要分为这几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
1. 定义资源
Sentinel
定义资源的方式有好几种,有抛出异常的方式、返回布尔值的方式、注解的方式等,我们不再逐个讲解,详情可以查看官方文档 basic-api-resource-rule (sentinelguard.io)
这里我们使用注解的方式来定义资源,Sentinel
提供了@SentinelResource
注解用于定义资源,并提供了AspectJ
的扩展用于自动定义资源、处理BlockException
等。
因为我们是通过 Spring Cloud Alibaba 接入的 Sentinel,则无需额外进行配置即可使用 @SentinelResource
注解,否则的话需要引入如下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>x.y.z</version>
</dependency>
我们来看一下注解的使用案例
@Service
public class IUserServiceImpl implements IUserService {
public static final String RESOURCE_NAME = "selectUserByName";
@Override
@SentinelResource(value = RESOURCE_NAME, blockHandler = "selectUserByNameBlockHandler", fallback = "selectUserByNameFallback")
public String selectUserByName(String username) {
return "{"userName": " + username + ", "age": 25}";
}
// 服务流量控制处理,注意细节,一定要跟原函数的返回值和形参一致,并且形参最后要加个BlockException参数,否则会报错,FlowException: null
public String selectUserByNameBlockHandler(String username, BlockException ex)
{
System.out.println("selectUserByNameBlockHandler异常信息:" + ex.getMessage());
return "{"code":"500","msg": "" + username + "服务流量控制处理"}";
}
// 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数
public String selectUserByNameFallback(String username, Throwable throwable)
{
System.out.println("selectUserByNameFallback异常信息:" + throwable.getMessage());
return "{"code":"500","msg": "" + username + "服务熔断降级处理"}";
}
}
@SentinelResource
注解包含以下属性:
参数 | 描述 |
---|---|
value | 资源名称,必需项(不能为空) |
entryType | 资源调用方向,可选项(默认为EntryType.OUT ) |
resourceType | 资源的分类 |
blockHandler | 对应处理BlockException 的函数名称 |
blockHandlerClass | 处理类的Class 对象,函数必需为static 函数 |
fallback | 用于在抛出异常的时候提供fallback 处理逻辑 |
defaultFallback | 用作默认的回退的方法 |
fallbackClass | 异常类的Class 对象,函数必需为static 函数 |
exceptionsToTrace | 异常类跟踪列表(默认为Throwable.class) |
exceptionsToIgnore | 排除掉的异常类型 |
注意:注解方式埋点不支持 private 方法。
注意:
-
blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。blockHandler 函数访问范围需要是public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException
。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的Class
对象,注意对应的函数必需为 static 函数,否则无法解析。
官方注解支持文档:注解支持 · alibaba/Sentinel Wiki · GitHub
2. 定义规则
@SpringBootApplication
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class, args);
initFlowQpsRule();
}
//定义了每秒最多接收2个请求
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule(IUserServiceImpl.RESOURCE_NAME);
// set limit qps to 2
rule.setCount(2);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
注意 FlowRule rule = new FlowRule(IUserServiceImpl.RESOURCE_NAME) 资源名称
流量控制规则(FlowRule
)重要属性
参数 | 描述 | 描述 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
limitApp | 流控针对的调用来源,若为 default 则不区分调用来源 | default,代表不区分调用来源 |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
count | 限流阈值 | |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流量控制效果(直接拒绝、Warm Up、匀速排队) | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
3. 检验规则是否生效
@RestController
public class UserController {
@Autowired
private IUserService userService;
@GetMapping("/user/getUserByName")
public String getUserByName(String userName){
return userService.selectUserByName(userName);
}
}
浏览器访问地址 localhost:9201/user/getUserByName?userName=小黑
快速刷新几次接口之后触发限流
同时观察控制台簇点链路
总结
Sentinel
的功能强大,肯定不是我们一篇文章能容纳的,此文只是提供了一个入门案例,后面会继续学习 Sentinel
的其他知识。
PS: 都看到这里了,点个赞吧,彦祖!
上一篇: 服务监控:稳定业务运营的关键
下一篇: 智能综合服务保障平台:应用与实践