一、Sentinel(哨兵)简介
1、Sentinel的功能及特点
1.Sentinel的功能
2.Sentinel的特点
2、Sentinel的组成
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 8 及以上的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。核心库不依赖控制台。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。提供了机器自发现、簇点链路自发现、监控、规则配置等功能。
3、Sentinel的内部结构及工作流程
1.内部核心类
1.Entry
每一次资源调用都会创建一个 Entry,包含了资源名、curNode(当前统计节点)、originNode(来源统计节点)等信息。
2.Context
Context是对资源操作的上下文,每个资源操作必须属于一个Context。如果代码中没有指定Context,则会创建一个name为sentinel_default_context的默认Context。一个Context生命周期中可以包含多个
3.Node
用于完成数据统计的接口
1、StatisticNode:统计节点,是Node接口的实现类,用于完成数据统计.
2、EntranceNode:入口节点,一个Context会有一个入口节点,用于统计当前Context的总体流量数据.
3、DefaultNode:默认节点,用于统计一个资源在当前Context中的流量数据.
4、ClusterNode:集群节点,用于统计一个资源在所有Context中的总体流量数据.
4.Slot
Slot中主要定义sentinel的功能,sentinel中默认提供了以下Slot
1、NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降。
2、ClusterBuilderSlot
用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count,Block count,Exception count 等等,这些信息将用作为多维度限流,降级的依据。简单来说,就是用于构建ClusterNode。
3、StatisticSlot
用于记录、统计不同纬度的 runtime 指标监控信息。
4、ParamFlowSlot
对应控制台的:热点流控
5、FlowSlot
用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制。对应控制台的:流控规则
6、AuthoritySlot
根据配置的黑白名单和调用来源信息,来做黑白名单控制。对应控制台的:授权规则
7、DegradeSlot
通过统计信息以及预设的规则,来做熔断降级。对应控制台的:降级规则
8、SystemSlot
通过系统的状态,例如 load1 等,来控制总的入口流量。对应控制台:系统规则
5.ProcessSlotChain
Sentinel 的核心骨架,将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起。
2.Sentinel设计和工作流程
Sentinel的核心骨架是 ProcessorSlotChain。 系统会为每个资源创建一套链路处理,将不同的 Slot(处理节点) 用责任链模式,将不同的功能组合在一起(限流、降级、系统保护)。
二、Sentinel(哨兵)使用
1、Sentinel 提供了多种定义资源方式
1.适配主流框架自动定义资源
2.通过 SphU 手动定义资源
3.通过 SphO 手动定义资源
4.注解方式定义资源
2、Sentinel控制台安装
下载地址:
https://github.com/alibaba/Sentinel/releases
启动(用户名和密码默认 sentinel,用8080默认端口号)
java -jar sentinel-dashboard-1.8.2.jar
3、代码使用
1.服务端引入依赖
<!-- Snetinel 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、服务端配置修改
spring:
application:
name: jun-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8800 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719
3.服务端启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudSentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudAlibabaSentinelService8401Application.class, args);
}
}
4、代码和控制台操作
熔断降级策略:
RT(平均相应时间,秒级)
当单位统计时长内,请求数目大于设置的最小请求数目,且慢调用的比例大于阈值。
异常比例(秒级)
当单位统计时长内,请求数目大于设置的最小请求数目且异常的比例大于阈值。
异常数(分钟级)
异常数(分钟级)超过阈值时,触发降级,时间窗口结束后关闭降级。
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphO;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@Slf4j
public class SentinelFlowLimitController {
@Value("${server.port}")
private String serverPort;
/**
* 通过 SphU 手动定义资源;try-catch 风格的 API。
*/
@GetMapping("/testA")
public String testA() {
Entry entry = null;
try {
entry = SphU.entry("testAbySphU");
//您的业务逻辑 - 开始
log.info("服务访问成功------testA:" + serverPort);
return "服务访问成功------testA:" + serverPort;
//您的业务逻辑 - 结束
} catch (BlockException e1) {
//流控逻辑处理 - 开始
log.info("testA 服务被限流");
return "testA 服务被限流";
//流控逻辑处理 - 结束
} finally {
if (entry != null) {
entry.exit();
}
}
}
/**
* 通过 SphO 手动定义资源;if-else 风格的 API。
*/
@GetMapping("/testB")
public String testB() {
if (SphO.entry("testBbySphO")) {
// 务必保证finally会被执行
try {
log.info("服务访问成功------testB:" + serverPort);
return "服务访问成功------testB:" + serverPort;
} finally {
SphO.exit();
}
} else {
// 资源访问阻止,被限流或被降级
//流控逻辑处理 - 开始
log.info("testB 服务被限流");
return "testB 服务被限流";
//流控逻辑处理 - 结束
}
}
/**
* 通过 @SentinelResource 注解定义资源。
*/
@GetMapping("/testC")
@SentinelResource(value = "testCbyAnnotation") //通过注解定义资源
public String testC() {
log.info("服务访问成功------testC:" + serverPort);
return "服务访问成功------testC:" + serverPort;
}
/**
* 通过 Sentinel 控制台定义流控规则
*/
@GetMapping("/testD")
@SentinelResource(value = "testD-resource", blockHandler = "blockHandlerTestD") //通过注解定义资源
public String testD() {
log.info("服务访问成功------testD:" + serverPort);
return "服务访问成功------testD:" + serverPort;
}
/**
* 限流之后的逻辑
*/
public String blockHandlerTestD(BlockException exception) {
log.info(Thread.currentThread().getName() + "TestD服务访问失败! 您已被限流,请稍后重试");
return "TestD服务访问失败! 您已被限流,请稍后重试";
}
}
5、纯代码控制接口
public abstract class CustomSentinelConfig {
@PostConstruct
private void config() {
List<FlowRule> flowRules = new ArrayList<>();
/**
* 添加限流方式 10次拒绝访问
*/
FlowRule flowRule1 = new FlowRule();
//资源名,资源名是限流规则的作用对象
flowRule1.setResource("限流-10");
//限流阈值
flowRule1.setCount(10);
//调用关系限流策略:直接、链路、关联
flowRule1.setStrategy(0);
//限流阈值类型,QPS 或线程数模式
flowRule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
//流控效果(直接拒绝 / 慢启动模式 / 排队等待),不支持按调用关系限流
flowRule1.setControlBehavior(0);
/**
* 添加限流方式 5次等待排队
*/
FlowRule flowRule2 = new FlowRule();
//资源名,资源名是限流规则的作用对象
flowRule2.setResource("限流等待-5");
//限流阈值
flowRule2.setCount(5);
//调用关系限流策略:直接、链路、关联
flowRule2.setStrategy(0);
//限流阈值类型,QPS 或线程数模式
flowRule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
//流控效果(直接拒绝 / 慢启动模式 / 排队等待),不支持按调用关系限流
flowRule2.setControlBehavior(2);
//超时时间设置
flowRule2.setMaxQueueingTimeMs(60000);
/**
* 添加限流方式 2次拒绝访问
*/
FlowRule flowRule3 = new FlowRule();
//资源名,资源名是限流规则的作用对象
flowRule3.setResource("限流-2");
//限流阈值
flowRule3.setCount(2);
//调用关系限流策略:直接、链路、关联
flowRule3.setStrategy(0);
//限流阈值类型,QPS 或线程数模式
flowRule3.setGrade(RuleConstant.FLOW_GRADE_QPS);
//流控效果(直接拒绝 / 慢启动模式 / 排队等待),不支持按调用关系限流
flowRule3.setControlBehavior(0);
/**
* 添加限流方式 10次等待排队
*/
FlowRule flowRule4 = new FlowRule();
//资源名,资源名是限流规则的作用对象
flowRule4.setResource("限流等待-10");
//限流阈值
flowRule4.setCount(10);
//调用关系限流策略:直接、链路、关联
flowRule4.setStrategy(0);
//限流阈值类型,QPS 或线程数模式
flowRule4.setGrade(RuleConstant.FLOW_GRADE_QPS);
//流控效果(直接拒绝 / 慢启动模式 / 排队等待),不支持按调用关系限流
flowRule4.setControlBehavior(2);
//超时时间设置
flowRule4.setMaxQueueingTimeMs(60000);
/**
* 添加限流方式 线程最多五个
*/
FlowRule flowRule5 = new FlowRule();
//资源名,资源名是限流规则的作用对象
flowRule5.setResource("线程-5");
//限流阈值
flowRule5.setCount(5);
//调用关系限流策略:直接、链路、关联
flowRule5.setStrategy(0);
//限流阈值类型,QPS 或线程数模式
flowRule5.setGrade(RuleConstant.FLOW_GRADE_THREAD);
//流控效果(直接拒绝 / 慢启动模式 / 排队等待),不支持按调用关系限流
flowRule5.setControlBehavior(0);
//超时时间设置
flowRule5.setMaxQueueingTimeMs(60000);
/**
* 组装限流
*/
flowRules.add(flowRule1);
flowRules.add(flowRule2);
flowRules.add(flowRule3);
flowRules.add(flowRule4);
flowRules.add(flowRule5);
FlowRuleManager.loadRules(currentLimitRules(flowRules));
}
/**
* 限流规则配置
*/
protected abstract List<FlowRule> currentLimitRules(List<FlowRule> rules);
}
@Configuration
public class SentinelConfig extends CustomSentinelConfig {
@Override
protected List<FlowRule> currentLimitRules(List<FlowRule> rules) {
return rules;
}
}
@RestController
public class SentinelController {
@SentinelResource(value = "线程-5",blockHandler = "blockHandlerTest")
@RequestMapping(value = "/myTest")
public String myTest(String id, HttpServletResponse response) throws Exception {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
return "访问结果";
}
public String blockHandlerTest(String id, HttpServletResponse response, BlockException b) throws Exception {
return "错误代码";
}
}