服务结合SkyWalking链路追踪参考:skywalking监控服务调用链路
sentinel github wiki:如何使用
随着微服务的流行,服务调用的稳定性变得越来越重要。Sentinel 以“流量”为切入点,在流量控制、熔断降级和负载保护等多个领域发挥作用,以保障服务的可靠性。
Sentinel 具有以下特点:
- 丰富的场景支持:Sentinel 已经支持阿里巴巴双 11 购物节的关键场景超过 10 年,例如秒杀(即控制突发流量,使其在系统容量的可接受范围内)、消息负载转移以及不可靠下游应用的熔断。
- 全面的实时监控:Sentinel 提供实时监控能力。您可以以秒级精度查看服务器的监控数据,甚至可以查看拥有不到 500 个节点的集群的整体运行状态。
- 广泛的开源生态系统:Sentinel 提供开箱即用的模块,可以轻松与 Spring Cloud、Dubbo 和 gRPC 等其他开源框架/库集成。要使用 Sentinel,您只需引入相关依赖并进行一些简单的配置。
- 健全的 SPI 扩展:Sentinel 提供易于使用且健全的 SPI 扩展接口。您可以通过 SPI 扩展快速定制逻辑,例如定义自己的规则管理,或者适配特定的数据源。
如何使用Sentinel
如果您想在您的项目中使用 Sentinel,请使用具有 com.alibaba.cloud
group ID和 spring-cloud-starter-alibaba-sentinel
artifact ID的启动器。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
下面是一个简单的代码示例:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
@RestController
public class TestController {
@GetMapping(value = "/hello")
@SentinelResource("hello")
public String hello() {
return "Hello Sentinel";
}
}
@SentinelResource
注解用于标识一个资源是否被限流或降级。在上述示例中,注解的 hello
属性指的是资源名称。
@SentinelResource
还提供了诸如 blockHandler
、blockHandlerClass
和 fallback
等属性,用于标识限流或降级操作。更多详情,请参考 Sentinel 注解支持。
上述示例均用于 WebServlet 环境。Sentinel 当前支持 WebFlux,需要与 spring-boot-starter-webflux
依赖项配合使用,以触发 Sentinel 启动器中的 WebFlux 相关自动化配置。
Sentinel 仪表板
Sentinel 仪表板是一个轻量级控制台,提供诸如机器发现、单机资源监控、集群资源数据概览以及规则管理等功能。这些配置的数据存储在服务的内存中,当服务重启之后规则会消失。要使用这些功能,您只需要完成几个步骤。
注意:集群的统计概览仅支持少于 500 个节点的集群,并且存在大约 1 到 2 秒的延迟。
可以通过github下载dashboard的jar包,启动命令:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel
。可以参考 鉴权模块文档 配置用户名和密码。
Sentinel实战
1. 添加依赖,并配置yaml
添加maven依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在application.yml文件中添加dashboard的配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8888
port: 8719
在 spring.cloud.sentinel.transport.port
中指定的端口号将在应用程序的相应服务器上启动一个 HTTP 服务器,该服务器将与 Sentinel 仪表板进行交互。例如,如果在 Sentinel 仪表板中添加了一个限流规则,那么规则数据将被推送到 HTTP 服务器并由其接收,随后该服务器会将规则注册到 Sentinel 中。
2. 启动dashboard
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.8.jar
3. 启动应用
controller代码编写:
@SpringBootApplication
@RestController
@RequestMapping("/stl")
public class SentinelApp {
public static void main(String[] args) {
SpringApplication.run(SentinelApp.class, args);
}
@GetMapping("/ping")
public String ping() {
return "pong";
}
}
应用运行在8080端口,并暴露了一个get /ping端点。
4. 查看dashboard
通过postman发送请求,并观察dashboard的情况。只有在发送请求之后,才会出现对应的服务菜单。

关于簇点链路
我们查看上面的簇点链路页签,可以看到系统自动为请求创建了一个请求路径的资源

这是因为所有访问的 Web URL 被自动统计为了 Sentinel 的资源,可以针对单个 URL 维度进行流控。若希望区分不同 HTTP Method,可以将 HTTP_METHOD_SPECIFY
这个 init parameter 设为 true,给每个 URL 资源加上前缀,比如 GET:/foo
。
异常处理
我们为其添加一个流控规则,将阈值设置为1:

然后请求查看被流控的效果:

这是sentinel默认的被流控后返回的错误信息。在开发中我们如果想自定义请求被流控后的异常信息,根据我们定义sentinel资源的不同,有以下四种方式:学习地址

针对于web接口的方式我们可以自定义BlockExceptionHandler实现并注入spring容器:
/**
* sentinel webmvc block handler
* <p/>
* see: {@link com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration#sentinelWebMvcConfig()}
*/
@Component
public class ErrorUnifyHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
PrintWriter out = response.getWriter();
out.print("被sentinel限流了");
out.flush();
out.close();
}
}
这样程序启动之后,再次被限流可以看到错误信息就是我们自定义的异常了。

动态规则源
Sentinel 目前支持以下数据源扩展:
拉模式拓展
实现拉模式的数据源最简单的方式是继承 AutoRefreshDataSource
抽象类,然后实现 readSource()
方法,在该方法里从指定数据源读取字符串格式的配置数据。比如 基于文件的数据源。
推模式拓展
实现推模式的数据源最简单的方式是继承 AbstractDataSource
抽象类,在其构造方法中添加监听器,并实现 readSource()
从指定数据源读取字符串格式的配置数据。比如 基于 Nacos 的数据源。
Nacos动态规则源
下面实战下nacos动态规则源的配置,nacos采用的则是推模式,在自动配置数据源的时候会同时注册nacos的监听器,当我们通过nacos管理界面修改配置之后,服务会收到对应的监听事件,从而获取到最新的规则配置。

1. 我们首先在nacos中添加对应的配置:

2. 在application.yml文件中添加对应的配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8888
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
data-id: sentinel-config.json
group-id: DEFAULT_GROUP
namespace: 9bf28ee1-daf9-4932-a453-a18de35a8be8
data-type: json
rule-type: flow
username: nacos
password: nacos
3. 添加maven依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
4. 之后启动项目即可,不需要额外的编写代码逻辑。我们在yml文件中的配置对应的是SentinelProperties
类。通过DataSourcePropertiesConfiguration
类也可以看到sentinel支持的动态规则源的集合,

并且每个配置方式中会有对应的factorybean类与之对应,用于bean的实例化。

使用spring boot的环境下,项目会自动配置SentinelDataSourceHandler
bean,在这个bean的实例构造完成的时候,通过生命周期方法org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated
,解析配置的datasource并逐一进行datasource bean的注册。
限流规则
流控规则
流控规则的流控模式有三种:直接,关联,链路。这里先来看看关联流控的效果,主要是要清楚两个资源谁影响谁:

这样配置规则的情况下:当资源二的QPS达到2的时候,对资源一的请求会被限流;可以统一理解为资源名是谁就是要流控谁。
热点参数规则
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
首先需要添加热点参数的依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
</dependency>
我们定义一个包含参数的请求GetMapping:
@GetMapping("/hp")
@SentinelResource("hot-parameter")
public String hotParameter(String p1, String p2) {
return "hot-parameter: " + p1 + ", " + p2;
}
在dashboard控制台中添加热点规则。

规则的含义是:资源hot-parameter对应的方法参数中,对第二个参数进行流量控制,2s内最多2个请求;例外值是“tom”,如果参数值是“tom”,则2s内的限流为10。
注意:目前 Sentinel 自带的 adapter 仅 Dubbo 方法埋点带了热点参数,其它适配模块(如 Web)默认不支持热点规则(也就是对于RequestMapping的请求路径自动生成的资源名不支持),可通过自定义埋点方式指定新的资源名并传入希望的参数。注意自定义埋点的资源名不要和适配模块生成的资源名重复,否则会导致重复统计。
工作流程SlotChain
先来看看什么是SlotChain,放出一张官网上面的图:

SlotChain是伴随我们的资源一同创建的,当使用Sphu.entry的时候会创建资源对应的Entry对象,同时也会创建一系列功能插槽(slot chain),这些插槽有不同的职责。当执行到FlowSlot节点的时候,会根据前面的slot节点统计的信息和当前资源配置的流控规则进行检查,如果被限流则抛出异常。
当所有的Slot判断完之后,才会开始执行我们的代码逻辑,也就是会执行Sphu.entry的下一行代码。
程序的入口则是在SphU类的方法上,从经常见的Sphu.entry(resourceName)
这个方法入手。当调用entry方法的时候并且传递一个资源名,该资源就代表着后续的调用行为,我们对资源名进行流控就是要对该资源所代表的执行逻辑进行流控:资源名==一段调用逻辑。
查看github wiki中的说明已经明确介绍了各个slot的作用,这里记录一下初始化SlotChain的流程。
我们从方法开始说起:
public static Entry entry(String name) throws BlockException {
return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
}
在方法体中调用了Env的sph静态字段的entry方法,里面有两部分:
- 实例化sph静态字段;
- 静态初始化块进行初始化;
public class Env {
public static final Sph sph = new CtSph();
static {
// If init fails, the process will exit.
InitExecutor.doInit();
}
}
这里静态初始化块中的部分比较简单,提一嘴即可:作用就是使用spi机制加载InitFunc接口的实现类,将它们按照order进行排序后循环遍历调用init方法。
官方的一个使用场景说明是:在动态规则拓展的时候需要将sentinel的规则存放在其他地方进行动态配置,此时可以使用InitFunc的方式,创建一个接口的实现类,在init方法中自己手动注册动态数据源。
SlotChain的初始化
接下来重点看的则是sph静态属性的实例化,在调用Sphu的方法(包括entry方法)的时候都会代理给这个属性去调用。entry()方法有很多重载,这里跟踪的是entry(String name)
这个方法的执行,顺着方法点进方法里面一直到entryWithPriority
方法,在这个方法中处理主要的逻辑步骤如下:
1. 初始化sentinel的上下文对象Context;
2. 使用SlotChainBuilder创建ProcessorSlotChain,并与resourcename进行映射保存在map中;
使用SlotChainBuilder
的build
方法创建的ProcessorSlotChain
对象,我们来看一下build
方法是如何进行构建的:
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
// 通过SPI机制读取ProcessorSlot的实现
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
continue;
}
// 将获取到的所有实现构造成链结构
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
因为这里的SlotChainBuilder
是通过spi机制获取到的,目前sentinel只有一个实现:DefaultSlotChainBuilder
,构造出的ProcessorSlotChain
是DefaultProcessorSlotChain
。AbstractLinkedProcessorSlot
类的是一个链式的结构,有一个first属性作为链的哨兵节点;next属性链接着下一个要执行的slot。
如果我们自定义ProcessorSlotChain
的实现,这里sentinel的要求是必须继承自AbstractLinkedProcessorSlot
类才能放入到slot chain中进行链式调用;
3. 将resource,context和chain统一封装为Entry对象,用于方法的返回值。
4. 调用SlotChain的entry方法,开始执行链逻辑;
下面列出了ProcessorSlot
中的预置slot:
com.alibaba.csp.sentinel.slotchain.ProcessorSlot
# Sentinel default ProcessorSlots
com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot
com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot
com.alibaba.csp.sentinel.slots.logger.LogSlot
com.alibaba.csp.sentinel.slots.statistic.StatisticSlot
com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot
com.alibaba.csp.sentinel.slots.system.SystemSlot
com.alibaba.csp.sentinel.slots.block.flow.FlowSlot
com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
预置的8种Slot的职责
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;ClusterBuilderSlot
则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;StatisticSlot
则用于记录、统计不同纬度的 runtime 指标监控信息;FlowSlot
则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;AuthoritySlot
则根据配置的黑白名单和调用来源信息,来做黑白名单控制;DegradeSlot
则通过统计信息以及预设的规则,来做熔断降级;SystemSlot
则通过系统的状态,例如 load1 等,来控制总的入口流量;
热点参数限流的实现方式就是拓展了ProcessorSlot
接口,提供了ParamFlowSlot
实现。