这里写目录标题
1.Sentinel概念
Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
官方文档:
https://github.com/alibaba/Sentinel/wiki
https://sentinelguard.io/zh-cn/docs/introduction.html
2.Sentinel工作原理
资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
3.Sentinel功能
流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
熔断降级
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。
Sentinel 对这个问题采取了两种手段:
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
4.Sentinel快速开始
4.1 基于API方式
1.引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.4</version>
</dependency>
2.测试代码
@RestController
@Slf4j
public class HelloController {
private static final String RESOURCE_NAME = "HelloWorld";
@RequestMapping(value = "/hello")
public String hello() {
try (Entry entry = SphU.entry(RESOURCE_NAME)) {
// 被保护的逻辑
log.info("hello world");
return "hello world";
} catch (BlockException ex) {
// 处理被流控的逻辑
log.info("blocked!");
return "被流控了";
}
}
/**
* 定义流控规则
*/
@PostConstruct
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// Set limit QPS to 20.
rule.setCount(1);
rules.add(rule);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
3.测试
http://localhost:8800/hello
访问一次请求通过,快速访问两次第二次被限制
4.2 基于注解方式
1.引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.4</version>
</dependency>
2.测试代码
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
@RestController
public class UserController {
// 只标注blockHandler 时默认方法要在当前类,且参数除了异常还需要方法原来的参数
@SentinelResource(value = "hello world",
blockHandler = "handleException",
fallback = "fallbackException")
@RequestMapping("/hello2")
public String hello2() {
//int i = 1 / 0;
return "helloworld";
}
public String handleException(BlockException ex){
return "被流控了";
}
public String fallbackException(Throwable t){
return "被异常降级了";
}
@RequestMapping(value = "/findOrderByUserId/{id}")
@SentinelResource(value = "findOrderByUserId",
// 除了标注 blockHandler 也需要指定类blockHandlerClass ,方法需要为静态方法
fallback = "fallback",fallbackClass = ExceptionUtil.class,
blockHandler = "handleException",blockHandlerClass = ExceptionUtil.class
)
public String findOrderByUserId(@PathVariable("id") Integer id) {
//ribbon实现
String url = "http://mall-order/order/findOrderByUserId/"+id;
String result = "静态方法熔断";
if(id==4){
throw new IllegalArgumentException("非法参数异常");
}
return result;
}
}
public class ExceptionUtil {
public static String fallback(Integer id,Throwable e){
return "===被异常降级啦===";
}
public static String handleException(Integer id, BlockException e){
return "===被限流啦===";
}
}
3.接入控制台
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.4</version>
</dependency>
4.启动控制台
#启动控制台命令
java -jar sentinel-dashboard-1.8.4.jar
用户可以通过如下参数进行配置:
- -Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;
- -Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;
- -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
4.3 Spring Cloud Alibaba整合Sentinel
1.引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.添加yml配置
server:
port: 8800
#暴露actuator端点 http://localhost:8800/actuator/sentinel
management:
endpoints:
web:
exposure:
include: '*'
spring:
application:
name: sentinel-demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 添加sentinel的控制台地址
dashboard: 127.0.0.1:8080
3.在sentinel控制台中设置流控规则(对资源的控制主要有流控和降级)
资源名: 接口的API
针对来源: 默认是default,当多个微服务都调用这个资源时,可以配置微服务名来对指定的微服务设置阈值
阈值类型: 分为QPS和线程数 假设阈值为10
QPS类型: 只得是每秒访问接口的次数>10就进行限流
线程数: 为接受请求该资源分配的线程数>10就进行限流