SpringCloud-Alibaba-Sentinel

1、概述

Sentinel,中文翻译为哨兵,是为微服务提供流量控制、熔断降级的功能,它和Hystrix提供的功能一样,可以有
效的解决微服务调用产生的“雪崩”效应,为微服务系统提供了稳定性的解决方案。随着Hytrxi进入了维护期,不
再提供新功能,Sentinel是一个不错的替代方案。通常情况,Hystrix采用线程池对服务的调用进行隔离,
Sentinel才用了用户线程对接口进行隔离,二者相比,Hystrxi是服务级别的隔离,Sentinel提供了接口级别的
隔离,Sentinel隔离级别更加精细,另外Sentinel直接使用用户线程进行限制,相比Hystrix的线程池隔离,减
少了线程切换的开销。另外Sentinel的DashBoard提供了在线更改限流规则的配置,也更加的优化。

从官方文档的介绍,Sentinel 具有以下特征:

丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控
制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,
甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、
gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻
辑。例如定制规则管理、适配数据源等。


 

2、基本使用

1、新建项目,添加pom依赖
    <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

2、编写配置文件
server:
  port: 9003
spring:
  application:
    name: cloud-sentinel-9003
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
    nacos:
      discovery:
        server-addr: xxx.xxx.xxx.xxx:8848
management:
  endpoints:
    web:
      exposure:
        include: "*"
***在这里,sentinel运行在本地,尝试使用docker,但是获取不到服务的详细链路,原因是sentinel主动向服务拉取信息,阿里云服务器无法访问到本机,因此失败。

3、启动项目后,访问两个端口/testA和/testB,刷新sentinel即可看到

3、流控规则

3.1、QPS直接快速失败

每秒如果访问超过五次,就会抛出异常

3.2线程数直接失败

当调用接口的线程数超过阀值时,进行限流,

3.3关联

当每秒访问HelloA超过一次后,将对HelloB进行限流,比如支付服务满了以后对订单服务进行限流,防止连带。

3.4链路

链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流;它的功能有点类似于针对 来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度 更细;

创建一个service,添加注解

@SentinelResource(value = "getOrder",blockHandler = "handlerException")

在流量监控处设置:

访问HelloB一秒超过一次就会限流。

注意:sentinel1.7以后需要设置spring.cloud.sentinel.web-context-unify=false即可

详情:https://github.com/alibaba/Sentinel/issues/1213

3.5预热

在预热的5秒内每秒的QPS最多是10/3=3次,如果超过3次就会限流,5秒以后阀值恢复到10

3.6排队等待

HelloA一秒处理一次请求,超过就排队等待,等待的超时时间为2000毫秒,超过等待毫秒就会限流。

4、服务降级

1、RT(平均响应时间,秒级)
    平均响应时间超出阀值且在时间窗口内通过的请求次数>=5次,两个条件同时满足后触发降级
    窗口期过后关闭断路器,RT最大4900

2、异常比例(秒级)
    QPS>=5且异常比例(秒级统计)超过阀值时,触发降级,时间窗口期结束后,关闭降级

3、异常数(分钟级)
    异常数(分钟统计)超过阀值时,触发降级;时间窗口结束后,关闭降级

4.1RT

一秒持续进入>=5次请求,平均响应时间如果大于200毫秒,则开启断路器,在时间窗口期2秒后关闭降级

4.2异常比例

一秒持续进入>=5次请求,如果每秒处理的请求异常比例大于百分之20,则开启断路器,在时间窗口期2秒后关闭降级

4.3异常数

异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

异常数是按分钟来统计的,所以时间窗口必须大于等于60s

在61秒内如果超过异常5次,则开启断路器,在61秒以后关闭断路器

5、热点Key

@GetMapping("/HelloE")
@SentinelResource(value = "helloE",blockHandler = "hostKeyHandler")
public String getE(@RequestParam(value = "a",required = false)String a
                  ,@RequestParam(value = "b",required = false)String b)
{
    return "------HelloE------";
}
public String hostKeyHandler(String a, String b, BlockException ex){
    return ex.getRule().getResource()+",服务正忙,稍后再试~";
}

对HelloE请求,携带的第一个参数,如果QPS超过1就会进行降级,返回自定义信息

添加参数例外项,设置如果第一个参数是2,则阀值是10,第一个参数不是2,则阀值是1

6、系统规则

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 
RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达
到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
Field 说明 默认值
highestSystemLoad load1 触发值,用于触发自适应控制阶段 -1 (不生效)
avgRt 所有入口流量的平均响应时间 -1 (不生效)
maxThread 入口流量的最大并发数 -1 (不生效)
qps 所有入口资源的 QPS -1 (不生效)
highestCpuUsage 当前系统的 CPU 使用率(0.0-1.0) -1 (不生效)

当任何请求访问系统QPS超过1,都会开启断路器

7、SentinelResource

7.1根据资源名进行限流

@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource()
{
    return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception)
{
    return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}

根据SentinelResource注解的Value进行限流

7.2根据URL进行限流

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
    return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}

根据Url进行限流,但使用的是Sentinel自己的提示信息,不友好

7.3自定义限流处理逻辑

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class,
        blockHandler = "handlerException2")
public CommonResult customerBlockHandler()
{
    return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}
public class CustomerBlockHandler {
  public static CommonResult handlerException2(BlockException exception)
  {
     return new CommonResult(4444,"按客戶自定义,global handlerException----1");
  }

将处理降级提示信息与业务逻辑分离,使用blockHandlerClass和blockHandler指明类名和方法名即可

SentinelResouce无法对private方法进行监控

8、服务熔断

1、新建两个服务提供者和一个消费者,将其注册到nacos

2、分别通过Ribbion和OpenFeign调用服务

pom依赖
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

8.1@SentinelResource只配置value

对value进行流控后,会报500错误

8.2@SentinelResource只配置fallback

只对系统异常进行降级处理,不会对sentinel设置的流控和降级进行兜底

8.3@SentinelResource只配置blockhandler

只对sentinel设置的流控和降级进行兜底处理,不会对系统异常进行降级处理

8.3@SentinelResource配置fallback和blockhandler

会对异常和sentinel设置的流控和降级进行不同的处理

8.4@SentinelResource中配置exceptionsToIgnore

会忽略设置的异常,不会进行兜底处理,可以自己写全局异常处理类进行处理

8.5OpenFeign通过在接口上添加注解,将降级逻辑和业务逻辑分离

新建类实现接口,在重载的方法里进行降级处理,在接口的注解加上fallback = xxx.class即可

注意需要将实现类加上@Component否则会报错

@RestController
@Slf4j
public class OrderController {

    @Resource
    private OrderService orderService;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${service.url.paymentUrl}")
    private String paymentUrl;

    // ----------OpenFeign-----------
    @GetMapping("/consumer/getByFeign/{id}")
    public CommonResult getPayByFeign(@PathVariable("id") Long id){
        log.info("**************OpenFeign");
        CommonResult<Payment> result = orderService.paymentSQL(id);
        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }
        return result;
    }
    // ----------Ribbion-----------
    @GetMapping("/consumer/getByRibbion/{id}")
    //@SentinelResource(value = "fallback")
    //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult getByRibbion(@PathVariable("id") Long id){
        log.info("**************Ribbion");
        CommonResult result = restTemplate.getForObject(paymentUrl + "/payment/" + id, CommonResult.class);
        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }
        return result;
    }
    //本例是fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }

}


------------------------------------------------------------
@Component
@FeignClient(value = "cloud-sentinel-payment-provider",fallback = OrderSerivceHandler.class)
public interface OrderService {

    @GetMapping(value = "/payment/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);

}

-----------------------------------------------------------------
@Component
public class OrderSerivceHandler implements  OrderService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

9、持久化规则

将限流配置持久化存储到Nacos里

1、添加pom依赖
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

2、在项目配置文件里添加配置如下
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
      datasource:
        wx1: #名字随意起
          nacos:
            server-addr: xxx.xxx.xxx.xxx:8848 #nacos地址
            dataId: cloud-alibaba-sentinel-service #配置文件的dataId
            groupId: DEFAULT_GROUP #配置文件的分组
            data-type: json #配置文件的类型
            rule-type: flow #用来定义存储的规则类型,flow代表限流规则
                            #degrade代表降级
                            #system代表系统规则
                            #authority代表授权
3、在Sentinel添加配置文件如下图

4、规则说明
resource:资源名称
limitApp:来源应用
grade:阀值类型,0代表线程数,1代表QPS
count:单机阀值
strategy:流控模式,0代表直接,1代表关联,2代表链路
controlBehavior:流控效果,0代表快速失败,1代表Warm Up(预热),2代表排队等待
clusterMode:是否集群,false代表没有,true代表集群

6、监控数据说明
流控的数据只会保存5分钟,如果想持久化,可以后期使用:
    1.实现 MetricsRepository 接口;
    2.注册成 Spring Bean 并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可。


10、其他说明

1、sentinel数据持久化有五种方式
    1.File 2.Redis 3.Nacos 4.Zookeeper 5.apollo

2、sentinel默认监控数据只保存五分钟,如果想要对监控的数据情况持久化有以下两种方式
    1.自行扩展实现 MetricsRepository 接口;
    2.注册成 Spring Bean 并在相应位置通过 @Qualifier 注解指定对应的 bean name 即可。
    

猜你喜欢

转载自blog.csdn.net/wx774891/article/details/106979572