Spring Cloud Alibaba实践 --Sentinel

sentinel介绍

Sentinel的官方标题是:分布式系统的流量防卫兵。从名字上来看,很容易就能猜到它是用来作服务稳定性保障的。对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第一反应应该就是Hystrix。但是比较可惜的是Netflix已经宣布对Hystrix停止更新。那么,在未来我们还有什么更好的选择呢?除了Spring Cloud官方推荐的resilience4j之外,目前Spring Cloud Alibaba下整合的Sentinel也是用户可以重点考察和选型的目标。

如果某一个服务不可用或者宕机了,就会出现线程池里所有线程都因等待响应而被阻塞, 从而造成服务雪崩。解决雪崩问题的常见方式,也是微服务常见的治理策略:

  • 服务超时:设定超时时间,请求超过一定的时间没有响应就返回错误信息,不会无休止等待。
  • 资源隔离:将系统按照一定的规则划分为若干个服务模块,各个模块之间相对独立,无强依赖。常见的隔离方式有:线程池隔离和信号量隔离。
  • 流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。
  • 服务熔断:有断路器统计业务执行的异常比例,如果超出阈值则会熔断业务,拦截访问该业务的一切请求,直到该业务错误率降到阈值以下。
  • 服务降级:当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。例如,在商品详情页一般都会展示商品的介绍信息,一旦商品详情页系统出现故障无法调用时,会直接获取缓存中的商品介绍信息返回给前端页面。

Sentinel 基本概念:

资源
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

流量控制
Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Spring Cloud Alibaba实践 --Sentinel_第1张图片

熔断降级

  • 通过并发线程数进行限制
    Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  • 通过响应时间对资源进行降级
    除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

Sentinel 的主要工作机制如下:

  • 对主流框架提供适配或者显示的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
  • 根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便您定义及改变规则。
  • Sentinel 提供实时的监控系统,方便您快速了解目前系统的状态。

Sentiner与hystrix的区别:
Spring Cloud Alibaba实践 --Sentinel_第2张图片

安装并整合sentinel客户端

Sentinel的使用分为两部分:

  • sentinel-dashboard:与hystrix-dashboard类似,但是它更为强大一些。除了与hystrix-dashboard一样提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能。
  • 客户端整合:每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。

(1)启动sentinel-dashboard

下载地址:sentinel-dashboard-1.6.0.jar
其他版本:Sentinel/releases

通过命令启动:

java -jar sentinel-dashboard-1.6.0.jar

默认情况下,sentinel-dashboard以8080端口启动。由于sentinel-dashboard是一个标准的spring boot应用,所以如果要自定义端口号等内容的话,可以通过在启动命令中增加参数来调整,比如:

java -jar sentinel-dashboard-1.6.0.jar --server.port=8888

所以可以通过访问:localhost:8888 来验证是否已经启动成功,如果一切顺利的话,可以看到登录页面(默认用户名和密码都是sentinel)。

(2)整合Sentinel

依赖:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

配置:

spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002

# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888

主类:

package com.example.demospringboot;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@SpringBootApplication
@Slf4j
public class DemospringbootApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemospringbootApplication.class, args);
	}

	@Slf4j
	@RestController
	static class TestController {

		@GetMapping("/hello")
		public String hello() {
			return "Hello World";
		}
	}
}

启动应用,并执行curl命令:
在这里插入图片描述
此时,在上面启动的Sentinel Dashboard中就可以当前我们启动的alibaba-sentinel-rate-limiting这个服务以及接口调用的实时监控了。具体如下图所示:
Spring Cloud Alibaba实践 --Sentinel_第3张图片

流控限流

配置接口限流规则

在完成了上面的两节之后,我们在alibaba-sentinel-rate-limiting服务下,点击簇点链路菜单,可以看到如下界面:

Spring Cloud Alibaba实践 --Sentinel_第4张图片

通过点击流控按钮,来为该接口设置限流规则,比如:

Spring Cloud Alibaba实践 --Sentinel_第5张图片

流控模式:

  • 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认模式。
  • 关联:统计与当前相关的另一个资源,触发阈值时,对当前资源限流。
  • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流,即只限流从指定接口进来的

流控效果:

  • 快速失效:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。

  • warm up:预热模式,对超出阈值的请求同样是拒绝,并抛出异常。但这种模式阈值会动态变化,从一个较小的值逐渐增加到最大值。请求阈值初始值是 threshold/coldFactor(默认值是3),持续指定时长后,逐渐提高到threshould值。例如,我设置QPS的threshold的为10,预热时间为5秒,那么初始阈值就是10/3,也就是3,然后在5秒后逐渐增长到10.

  • 排队等待:让所有请求按照先后次序进入一个队列中排队执行,两个请求的间隔不能小于指定时长。例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout =2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常。

参考案例

点击新增按钮之后,可以看到如下界面:
Spring Cloud Alibaba实践 --Sentinel_第6张图片

其实就是左侧菜单中流控规则的界面,这里可以看到当前设置的所有限流策略。在完成了上面所有内容之后,我们可以尝试一下快速的调用这个接口,看看是否会触发限流控制,比如:

Spring Cloud Alibaba实践 --Sentinel_第7张图片
可以看到,快速的调用/hello接口之后,调用被限流了。
在 sentinel-dashboard界面,也可以看到对应统计:
Spring Cloud Alibaba实践 --Sentinel_第8张图片

Sentinel使用Nacos存储规则

Dashboard中设置的限流规则在应用重启之后就丢失了。Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:

  • 文件配置
  • Nacos配置
  • ZooKeeper配置
  • Apollo配置

本文我们使用Spring Cloud Alibaba的中整合的配置中心Nacos存储限流规则。前置条件,启动Nacos,详见 Spring Cloud Alibaba实践 --Nacos

默认配置下启动后,它们的访问地址为:

  • Nacos:http://localhost:8848/nacos
  • Sentinel Dashboard:http://localhost:8080/

(1)在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Nacos存储扩展:

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
        <version>1.5.2</version>
    </dependency>

(2)在Spring Cloud应用中添加配置信息

spring.application.name=alibaba-sentinel-rate-limiting
server.port=8002
# sentinel dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8888

# ds-sentinel-nacos-datasource
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinel
spring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow

(3)Nacos中创建限流规则的配置

Spring Cloud Alibaba实践 --Sentinel_第9张图片

注:Data ID、Group就是上面第二步中配置的内容。配置格式选择JSON,并在配置内容中填入下面的内容:

[
    {
        "resource": "/hello",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:

  • resource:资源名,即限流规则的作用对象
  • limitApp:流控针对的调用来源,若为 default 则不区分调用来源
  • grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
  • count:限流阈值
  • strategy:调用关系限流策略
  • controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
  • clusterMode:是否为集群模式

(4)启动应用,curl localhost:8002/hello测试

如果一些顺利,可以看到类似下面的日志,代表已经成功从Nacos加载了一条限流规则:

2023-12-09 19:00:04.887  INFO 13800 --- [           main] o.s.c.a.s.c.SentinelDataSourceHandler    : [Sentinel Starter] DataSource ds-sentinel-nacos-datasource load 1 FlowRule

在Sentinel Dashboard中就可以看到当前我们启动的alibaba-sentinel-rate-limiting
服务。点击左侧菜单中的流控规则,可以看到已经存在一条记录了,具体如下:
Spring Cloud Alibaba实践 --Sentinel_第10张图片
注:在完成了上面的整合之后,对于接口流控规则的修改就存在两个地方了:Sentinel控制台、Nacos控制台。

这个时候,需要注意当前版本的Sentinel控制台不具备同步修改Nacos配置的能力,而Nacos由于可以通过在客户端中使用Listener来实现自动更新。所以,在整合了Nacos做规则存储之后,需要知道在下面两个地方修改存在不同的效果:

  • Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Nacos中的配置值,重启后恢复原来的值。
  • Nacos控制台中修改规则:服务的内存中规则会更新,Nacos中持久化规则也会更新,重启后依然保持。

@SentinelResource对某个方法资源调用限流

在实际应用过程中,我们可能需要限流的层面不仅限于接口,而是对于某个方法的调用限流,对于某个外部资源的调用限流等都希望做到控制。这个时候我们就不得不手工定义需要限流的资源点,并配置相关的限流策略等内容了。使用@SentinelResource注解可以灵活的定义控制资源以及如何配置控制策略。

主启动类:
在应用主类中增加注解支持的配置Bean

package com.example.demospringboot;

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@Slf4j
public class DemospringbootApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemospringbootApplication.class, args);
	}

	// 注解支持的配置Bean
	@Bean
	public SentinelResourceAspect sentinelResourceAspect() {
		return new SentinelResourceAspect();
	}

}

Service:
在需要通过Sentinel来控制流量的地方使用@SentinelResource注解,自定义资源点;
并通过@SentinelResource注解的blockHandler属性实现限流的异常处理

package com.example.demospringboot;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TestService {
    @SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler")
    public void doSomeThing(String str) {
        log.info(str);
    }

    // 限流与阻塞处理
    public void exceptionHandler(String str, BlockException ex) {
        log.error( "blockHandler-> " + str, ex);
    }
}

Controller:
在Web层调用这个被保护的方法

package com.example.demospringboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@RestController
public class TestController {
    @Autowired
    private TestService testService;

    @GetMapping("/hello")
    public String hello() {
        testService.doSomeThing("hello " + new Date());
        return "Hello World";
    }
}

启动测试应用,启动Sentinel-Dashboard。 curl localhost:8002/hello发一个请求到/hello接口上,使得Sentinel-Dashboard上可以看到如下图所示的几个控制点:

Spring Cloud Alibaba实践 --Sentinel_第11张图片

可以看到,多了一个doSomeThing资源点。可以通过界面为这个资源点设置限流规则,比如将其QPS设置为2。由于/hello资源不设置限流规则,所以只要请求/hello接口,就可以直接模拟调用doSomeThing资源,来观察限流规则是否生效。只要QPS超过2,那么就会出现如下的错误返回,代表限流策略生效了。

2023-12-09 12:45:58.015  INFO 11368 --- [nio-8002-exec-7] com.example.demospringboot.TestService   : hello Sat Dec 09 12:45:58 CST 2023
2023-12-09 12:45:58.378 TRACE 11368 --- [nio-8002-exec-8] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 12:45:58.401 ERROR 11368 --- [nio-8002-exec-8] com.example.demospringboot.TestService   : blockHandler-> hello Sat Dec 09 12:45:58 CST 2023

com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

熔断降级

@SentinelResource注解除了可以用来做限流控制之外,还能实现与Hystrix类似的熔断降级策略。

这里只需对TestService类改造,让doSomeThing方法持续抛出异常,触发fallback属性指定的具体方法名进行降级处理。

package com.example.demospringboot;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class TestService {
    @SentinelResource(value = "doSomeThing", fallback = "fallbackHandler")
    public void doSomeThing(String str) {
        log.info(str);
        throw new RuntimeException("发生异常");
    }

    public void fallbackHandler(String str) {
        log.error("fallbackHandler-> " + str);
    }
}

在Sentinel-Dashboard名为doSomeThing的资源点上,点击”降级“按钮,为该资源设置降级规则。这里使用异常比例策略,比例设置为0.1(即:10%的异常率),时间窗口设置为2(秒):
Spring Cloud Alibaba实践 --Sentinel_第12张图片
验证熔断降级,根据上面的降级策略配置,当doSomeThing方法的调用QPS >= 5时,如果异常率超过10%,那么后续2秒内的调用将直接触发熔断降级:

2023-12-09 13:30:21.332 TRACE 4248 --- [io-8002-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.333 ERROR 4248 --- [io-8002-exec-10] com.example.demospringboot.TestService   : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023
2023-12-09 13:30:21.678 TRACE 4248 --- [nio-8002-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demospringboot.TestController#hello()
2023-12-09 13:30:21.678 ERROR 4248 --- [nio-8002-exec-2] com.example.demospringboot.TestService   : fallbackHandler-> hello Sat Dec 09 13:30:21 CST 2023

参考:
https://www.didispace.com/spring-cloud/spring-cloud-alibaba-sentinel-2-5.html
https://zhuanlan.zhihu.com/p/565074363?utm_id=0

你可能感兴趣的:(云计算虚拟化,JAVA进阶,sentinel)