SpringCloud-(四)Sentinel的搭建与使用

本文最后更新于:May 13, 2023 pm

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

目录

官方下载地址:https://github.com/alibaba/Sentinel/releases

下载后就可以直接放在项目中。

配置启动

在IDEA中配置一个 JAR Application。和正常配置多应用一样。可自行在环境变量中设置启动端口。

登录

访问刚才设置的端口,登录时的用户名和密码均为:sentinel

sentinel的配置文件内容(官方源码):

1
2
3
4
5
6
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8080

修改默认参数

参数说明:(注意:参数要放到 -jar的前边)

1
java -Dserver.port=8480 -Dcsp.sentinel.dashboard.server=localhost:8480 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.7.2.jar

个人服务器上部署使用:

1
java -Dserver.port=8091 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=admin -jar sentinel-dashboard-1.8.5.jar &

解释:

  • -Dserver.port=8480 # 指定控制台的端口为8480
  • -Dcsp.sentinel.dashboard.server=localhost:8480 # 指定要被哪个控制台监控(这里指定的是自己监控自己)
  • -Dproject.name=sentinel-dashboard # 指定实例名称(名称会在控制台左侧以菜单显示)
  • -Dsentinel.dashboard.auth.username=sentinel # 设置登录的帐号为:sentinel
  • -Dsentinel.dashboard.auth.password=123456 # 设置登录的密码为:123456

官方地址:

使用

首次进入,会发现只有一个首页,是因为还没有客户端连接到控制台。接下来就将需要监控的模块进行设置。

添加依赖

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

添加监控地址

如下所示,我这里是配置bootstrap.yml中,但都是一样的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
spring:
application:
name: admin-service # 设置的服务名称
profiles:
# nacos的配置中心中配置的文件环境,环境部分
active: dev

cloud:
loadbalancer:
nacos:
enabled: true
nacos:
config:
# 配置文件后缀名
file-extension: yaml
# nacos服务地址
server-addr: localhost:80

sentinel:
transport:
dashboard: localhost:8858
clientIp: localhost
port: 8719 # 默认值

📢注意:如果是在服务器上部署,clientIp和port必须指定(port不指定默认8719),否则 ‘实时监控’ 中无数据。因为上面的源码中写的是本地地址。可查看服务器上的日志中报错连接超时的IP和dashboard的机器列表中的IP进行对比,发现是一样的,都是本地地址。而之所以连接超时,就是因为没有配置clientIp。

还需要注意的是:如果有多个应用都是同一个clientIp,则端口不能一样,默认会将已占的端口号+1来进行设置。所以需要保证服务器端口号要被打开。

测试

再次访问sentinel监控页面。必须要访问一次所监控的模块(不管是直接调用还是被其他模块调用),否则无法进行监控。(懒加载)

注意事项

搭建的应用与部署的Sentinel必须要能够互相进行通信。不能是:一个在服务器上,一个在本地。这样无法保证两者互相通信。

流量控制

限流策略

有三种策略:快速拒绝、预热(动态调整)、排队等待。

  • 快速拒绝:当用户访问过快到达某一个阈值时(每秒访问多少次),不再接受新的请求,直接返回一个拒绝信息,告诉用户访问频率过快。
  • 预热(动态调整):可能很多情况下,大量的访问都是某一时间段内突然来,而如果采用上面的方法,那么会造成很多用户无法访问。所以,可以根据某一段时间内的访问量来适当的提高阈值。可以缓慢的将阈值提高到指定阈值,形成一个缓冲保护。
  • 排队等待。当达到阈值时,不再接受请求,但也不是直接拒绝掉。而是在等待规定时间后,如果可以执行就执行,否则就拒绝。

Sentinel设置限流

增加规则:

  • 单机阈值:即每台每秒的访问次数。
  • 流控模式-直接:只针对当前接口。
  • 流控模式-关联:当其他给定接口(关联资源)访问超过阈值时,会导致当前接口限流。(别人做坏事,你背锅)
  • 流控模式-链路:更细粒度的限流,能精确到具体的方法。

新增完成后,在 流控规则 里面可以进行查看。

流控模式-链路

给某一具体方法进行限流,实现任何调用此方法的接口都会进行限流。触发限流会抛出异常。也可以自定义设置某些接口访问时不触发限流。

实现使用注解@SentinelResource,默认情况会以方法名作为资源名称,也可以自定义名称,如下。

1
2
3
4
5
6
7
@SentinelResource("test")
@RequestMapping("/c")
public Object getT() {
return new JSONObject()
.fluentPut("ok", "qwerqwer")
;
}

然后添加配置:

1
2
3
4
5
6
7
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8091 #配置sentinel dashboard地址
web-context-unify: false # 关闭Context收敛,这样被监控的方法可以进行不同链路的单独控制。

然后重启应用,访问一次后,即可在Sentinel中看见自己命名(或者是方法名)的资源名,这里是 test。然后就可以正常的以资源名进行限流的配置。

不同链路控制

在上述资源名(test)的限流配置中,选择 流控模式-链路,然后会让填写一个入口资源,这个入口资源即为被限制的链路,即:调用此入口资源接口,如果经过了被限制的方法,就会进行限流。

  • 入口资源:调用此接口,会经过被限流的方法。

还可以实现根据系统资源(CPU等)的使用情况进行限流,配置 系统规则 即可。这里不再叙述。

限流和异常处理

接口限流

创建被限流时的请求映射:

1
2
3
4
@RequestMapping("/quickError")
public Object quick() {
throw new BizRuntimeException(BizErrors.ERROR); // 自定义异常类和进行了全局的异常处理
}

具体自定义异常类和全局异常处理可参考:SpringBoot-(二十一)高级统一结果的封装和全局异常的处理

该方法将会在触发限流时被调用。接下来就是配置限流时需要映射的接口:

1
2
3
4
5
6
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8091 #配置sentinel dashboard地址
block-page: /adminT/admin/quickError # 限流时触发的接口

注意:这里的block-page的地址为:配置文件的配置前缀地址+Controller接口类上的地址+方法上面的地址。

方法限流

@SentinelResource

  • value:指定在Sentinel中显示的资源名称。
  • blockHandler:触发限流时的映射方法名称。
1
2
3
4
5
6
7
8
9
10
11
@SentinelResource(value = "queryAll",blockHandler = "quick")
@RequestMapping("/queryAll")
public Object queryAll() {
return new JSONObject()
.fluentPut("total", adminService.queryAll())
;
}

public Object quick(BlockException e) {
throw new BizRuntimeException(BizErrors.ERROR);
}

需要注意:参数和返回值要和被限流方法保持一致。但可以在其后面加一个BlockExeception异常。另外,只有限流的异常才会被处理,其他的异常并不会进行处理。如果是想实现,只要是异常就执行替换方案,见下。

然后去Sentinel中配置对应资源名称的限流规则。

处理任何异常处理

fallback:出现异常时的替代方案。这时,无需再在Sentinel中进行配置。

1
2
3
4
5
6
7
8
9
@SentinelResource(value = "queryAll",fallback = "quick")
@RequestMapping("/queryAll")
public Object queryAll() {
throw new NullPointerException();
}

public Object quick(Throwable e) {
throw new BizRuntimeException(BizErrors.ERROR);
}

其中可用参数:

  • exceptionsToIgnore=xss.Class:忽略哪些异常。
  • 📢注意:如果 blockHandler 和 fallback 同时出现,则会执行 blockHandler ,因为限流是在方法执行前进行的。

热点参数限流

根据请求的参数进行限流。

1
2
3
4
5
6
7
8
9
@SentinelResource("param")
@RequestMapping("/getP")
public Object getP(@RequestParam(value = "a",required = false) Integer a,
@RequestParam(value = "b",required = false) Integer b,
@RequestParam(value = "c",required = false) Integer c) {
return new JSONObject()
.fluentPut("ok", "a:" + a + " b: " + b + "c:" + c)
;
}

然后在Sentinel控制台的 热点规则 中进行配置。资源名称就是@SentinelResource注解中的值。其他需要注意的:

  • 参数索引:即第几个参数。

设置例外值

如果想再自定义根据参数的值进行限流,则使用高级选项中的选项进行额外的限流判断。

  • 参数值:当为该值时限流。
  • 限流阈值:当每秒访问次数到达改值时进行限流。

例如:限流第一个参数,每秒只能访问一次,但如果该参数为3,则每秒可以访问2次。

服务熔断与降级

服务降级:当下游服务因为某种原因变得不可用或响应过慢时,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,而是快速返回或是执行自己的替代方案。

熔断器的三个状态:

  • 关闭:熔断器不工作,所有请求全部正常执行。最开始的状态。
  • 打开:判断是否超过规定阈值(设置一定的阈值从而防止偶然异常情况),超过则熔断器工作,所有请求一律降级处理。
  • 半开:经过一定的时间后尝试请求是否正常,如果成功则关闭熔断器,否则继续打开。

打开Sentinel查看熔断规则,新建一个规则。参数介绍:

熔断策略的三种模式:

  • 慢调用比例:如果出现半天都处理不完的调用,有可能是服务器故障导致卡顿。而慢调用的判定:如果某一次请求超过了指定的最大RT(最大相应时间),就会被判定为慢调用。如果在某一时间段内(统计时长)的请求数大于规定的最小请求数(如图:必须是1秒内请求总数超过5次,这是前提。如果没有达到,即使超过比例阈值也不会熔断。),并且这部分请求中被判定为慢调用的数量比例超过比例阈值,那么将会触发熔断。在经过熔断时长后,将会进入半开状态进行重试(尝试请求访问)。如果请求通过则熔断器关闭,否则继续打开。再依次循环等待与重试。(简单说就是:看失败的概率。)
  • 异常比例:和慢调用比例类似,只是这里判断的是出现异常的次数。
  • 异常数:达到指定的异常数量就进行熔断。

降级

可以采用前面限流的那种替代方式实现,即使用注解@SentinelResource实现。

使用Feign对接口进行降级

之前使用@FeignClient实现了接口间的调用,现在来实现当接口挂掉时,进行降级处理,采用备用方案。

添加配置:

1
2
3
feign: # 单独的,并没有在spring中
sentinel:
enabled: true
示例

原接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.tothefor.adminapi.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* @Author DragonOne
* @Date 2022/9/15 23:45
* @Title
* @Description
*/
@FeignClient(value = "user-service",fallback = UserApiClientRallback.class)
public interface UserApiClient {
@RequestMapping("/userT/user/c")
String getT();
}

fallback写备用方案的类。

备用方案(降级处理):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.tothefor.adminapi.client;

import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/9/18 19:52
* @墨水记忆 www.tothefor.com
*/
@Component
public class UserApiClientRallback implements UserApiClient{
@Override
public String getT() {
return "我是备用方案";
}
}

然后开启admin-service服务,关掉user-service服务。然后再进行访问。