• ​​微服务系列:服务网关 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. 定义资源
  2. 定义规则
  3. 检验规则是否生效

看了上面这么多概念,感觉头都要秃了,我们还是来实战试试吧。

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​​ 指定控制台端口

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_Sentinel

启动成功浏览器访问地址 http://localhost:8718 ,默认用户名密码:​​sentinel/sentinel​

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_微服务_02

登录进去之后是这样的,空空如也,接下来我们就要看看到底如何使用了。

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_Sentinel_03

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、启动项目,访问控制台

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_微服务_04

访问 ​​Sentinel​​ 控制台发现还是空空如也,这是怎么回事呐?

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_微服务_05

原来​​Sentinel​​控制台有懒加载机制,我们需要访问一次我们服务的接口,控制台才会出现相关信息

我们来访问一下接口 ​​localhost:9201/testA​​

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_流量控制_06

再次刷新 ​​Sentinel​​ 控制台,发现出现了很多请求相关信息

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_流量控制_07

我们也可以在配置文件中配置 spring.cloud.sentinel.eager: true 属性来取消控制台懒加载

这一步,到这里项目只是成功集成了 ​​Sentinel​​ 并打开了控制台,关于控制台上的相关操作,此文暂不讨论,下篇文章开始学习。

使用案例

上面有说过,使用 Sentinel 来进行资源保护,主要分为这几个步骤:

  1. 定义资源
  2. 定义规则
  3. 检验规则是否生效

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=小黑​​

快速刷新几次接口之后触发限流

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_微服务_08

同时观察控制台簇点链路

微服务系列:Spring Cloud Alibaba 之 Sentinel 详细入门_流量控制_09

总结

​Sentinel​​​ 的功能强大,肯定不是我们一篇文章能容纳的,此文只是提供了一个入门案例,后面会继续学习 ​​Sentinel​​ 的其他知识。

PS: 都看到这里了,点个赞吧,彦祖