当前位置: 首页 > news >正文

【微服务架构】SpringCloud Alibaba(五):服务容错 Sentinel(降级规则、热点规则、授权规则、系统规则、集群规则)

文章目录

  • 一、SpringCloud Alibaba
    • 1、核心组件
    • 2、优势
    • 3、应用场景
  • 二、Sentinel规则(dashboard)
    • 2.1 降级规则
      • 2.1.1 慢调用比例
      • 2.1.2 异常比例
      • 2.1.3 异常数
    • 2.2 热点规则
    • 2.3 授权规则
      • 2.3.1 在feign中将参数设置进去
    • 2.4 系统规则
    • 2.5 集群规则

在这里插入图片描述
个人主页:道友老李
欢迎加入社区:道友老李的学习社区

一、SpringCloud Alibaba

Spring Cloud Alibaba是阿里巴巴提供的一站式微服务解决方案,是Spring Cloud体系中的一个重要分支,它将阿里巴巴在微服务领域的实践经验和开源技术进行了整合,为开发者提供了一系列便捷的工具和组件,用于构建分布式微服务应用。以下是其详细介绍:

1、核心组件

  • Nacos:用于服务注册与发现以及配置管理。它可以帮助微服务实例自动注册到注册中心,并能够动态获取配置信息,使应用程序能够灵活地应对配置的变化,无需重启服务。
  • Sentinel:主要用于流量控制、熔断降级等功能。它可以保护微服务免受高并发、流量异常等情况的影响,确保系统在压力下能够稳定运行,避免因个别服务出现问题而导致整个系统崩溃。
  • RocketMQ:是一款高性能、高可靠的分布式消息队列。它在微服务架构中常用于实现异步消息传递、解耦系统组件之间的依赖关系,从而提高系统的整体性能和可扩展性。
  • Seata:致力于提供分布式事务解决方案,确保在分布式系统中数据的一致性。它通过对事务的协调和管理,使得多个微服务之间在进行数据交互时能够遵循ACID原则。

2、优势

  • 一站式解决方案:涵盖了微服务架构中的多个关键领域,包括服务治理、配置管理、流量控制、分布式事务等,开发者无需再从多个不同的开源项目中进行整合,大大降低了微服务架构的搭建和维护成本。
  • 与Spring Cloud生态的深度集成:基于Spring Cloud的编程模型和规范进行开发,使得熟悉Spring Cloud的开发者能够快速上手并轻松集成到现有的Spring Cloud项目中,充分利用Spring Cloud的各种特性和优势。
  • 阿里巴巴的技术实力和实践经验支持:得益于阿里巴巴在大规模分布式系统开发和运营方面的丰富经验,Spring Cloud Alibaba的组件经过了实际生产环境的考验,具有较高的稳定性、性能和可扩展性,能够应对各种复杂的业务场景和高并发流量。

3、应用场景

  • 电商系统:在电商业务中,存在多个微服务,如商品服务、订单服务、库存服务等。Spring Cloud Alibaba可以通过Nacos进行服务注册与发现,使用Sentinel对各个服务的流量进行控制,利用RocketMQ实现异步消息通知,比如下单成功后异步通知库存服务扣减库存,通过Seata保证分布式事务的一致性,确保订单和库存等数据的准确性。
  • 金融系统:金融领域对数据一致性和系统稳定性要求极高。Spring Cloud Alibaba的Seata可以确保在多个金融业务操作之间的分布式事务一致性,如转账操作涉及到两个不同账户服务之间的资金变动。Nacos可以提供配置管理,方便对金融业务的各种配置参数进行动态调整,Sentinel则可以防止因突发的高并发交易对系统造成冲击。
  • 物联网(IoT)平台:物联网场景中,大量的设备会产生实时数据并上传到云端。Spring Cloud Alibaba可以通过Nacos管理各个物联网服务的注册与发现,使用RocketMQ接收和处理大量的设备数据消息,进行异步处理和分发。Sentinel可以对物联网服务的流量进行控制,防止因设备数据突发增长导致系统过载。

二、Sentinel规则(dashboard)

2.1 降级规则

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

熔断降级规则说明熔断降级规则(DegradeRule)包含下面几个重要的属性:

Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count最小请求数,慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount最小请求数,熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长,(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold比例阈值,慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:平均响应时间、异常比例、异常数。

2.1.1 慢调用比例

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

  1. 代码

    @RequestMapping("/slowRequestRatio")
    public String slowRequestRatio(){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "熔断降级==慢调用比例";
    }
    
  2. 流控规则

    image.png

  3. jmeter测试

    image.png

  4. 查看监控图

    查看效果发生流控,每两秒重试一次,我们可以通过查看jemter中查看结果树来判断, 会发现有个重试,每次重试间隔应该是两秒

    image.png

2.1.2 异常比例

当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

  1. 代码

    // 下面这个方式有待研究
    private AtomicInteger atomicInteger =new AtomicInteger(1);
    @RequestMapping("/errorRatio")
    public String errorRatio(){
        atomicInteger.getAndIncrement();
        log.info("现在的数值:{}",atomicInteger.get() );
        if (atomicInteger.get() % 2 == 0){
            //模拟异常和异常比率
            int i = 1/0;
        }
        return "熔断降级==异常比例";
    }
    
    
  2. 流控规则

    image.png

  3. jmeter测试

    image.png

  4. 查看监控图

image.png

2.1.3 异常数

当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

<font color= “red”>注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。</font>

  1. 代码

    @RequestMapping("/errorCount")
    public String errorCount(){
        atomicInteger.getAndIncrement();
        if (atomicInteger.get() % 2 == 0){
            //模拟异常和异常比率
            int i = 1/0;
        }
        return "熔断降级==异常数量";
    }
    
  2. 流控规则

    image.png

  3. jmeter测试

    image.png

  4. 查看监控图

image.png

2.2 热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

<font color=“red”>热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。</font>

image.png

  1. 代码

    //http://localhost:8001/hotspot/123
    @RestController
    public class HotSpotRuleController {
    
        @RequestMapping("/hotspot/{id}")
        @SentinelResource(value = "hotspot",
                blockHandlerClass = CommonException.class,
                blockHandler = "handleException",
                fallbackClass = CommonException.class,
                fallback = "fallback"
        )
        public Result info(@PathVariable("id") Integer id){
            return Result.ok("成功获取数据");
        }
    }
    

    注意:

    1. 热点规则需要使用@SentinelResource(“resourceName”)注解,否则不生效
    2. 参数必须是7种基本数据类型才会生效
  2. 流控规则

    注意: 资源名必须是@SentinelResource(value=“资源名”)中 配置的资源名,热点规则依赖于注解

    image.png

    这里有个坑,经过上面的配置只是对接口的限流,如果具体到参数需要再热点规则中重新设置参数

    具体到参数值限流,配置参数值为1,限流阈值为2

    image.png

  3. jmeter测试

    image.png

  4. 查看监控图

image.png

2.3 授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单,则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

例如:活动和订单都会调用用户系统获取用户信息,我们可以将活动设置为黑名单

image.png

image.png

上面的【资源名】和【授权类型】不难理解,但是【流控应用】怎么填写呢?

其实这个位置要填写的是来源标识,Sentinel提供了RequestOriginParser 接口来处理来源。

只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser 的实现类去解析访问来源。

第1步: 自定义来源处理规则

//http://localhost:8001/grant
@RestController
public class GrantController {

    @RequestMapping("/grant")
    public Result grant(){
        return Result.ok("授权规则测试成功");
    }
}
@Component
public class RequestOriginParserDefinition implements RequestOriginParser{
    @Override
    public String parseOrigin(HttpServletRequest request) { 
        String serviceName = request.getParameter("serviceName"); 
        return serviceName;
    }
}

第2步: 授权规则配置

这个配置的意思是只有serviceName=order不能访问(黑名单)

image.png

第3步: 访问 http://localhost:8001/grant?serviceName=order观察结果

2.3.1 在feign中将参数设置进去

  1. dyll-order项目中光阴如引入feign的代码可以参看Feign的文档

    public class FeignAuthRequestInterceptor  implements RequestInterceptor {
        private String serviceName;
    
        public FeignAuthRequestInterceptor(String serviceName) {
            this.serviceName = serviceName;
        }
        @Override
        public void apply(RequestTemplate template) {
            template.header("serviceName",serviceName);
        }
    }
    
    @Configuration
    public class FeignConfig {
        @Value("${spring.application.name}")
        private String serviceName;
    
        /**
         * 自定义拦截器
         * @return
         */
    
        @Bean
        public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){
            return new FeignAuthRequestInterceptor(serviceName);
        }
    }
    
  2. 在dyll-stock项目中增加配置

    @Component
    public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
    String serviceName = request.getHeader(“serviceName”);
    return serviceName;
    }
    }

可以对异常进行包装

package com.dyll.order.handle;
@Slf4j
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        log.info("BlockExceptionHandler BlockException================"+e.getRule());

        Result r = null;

        if (e instanceof FlowException) {
            r = Result.error(100,"接口限流了");

        } else if (e instanceof DegradeException) {
            r = Result.error(101,"服务降级了");

        } else if (e instanceof ParamFlowException) {
            r = Result.error(102,"热点参数限流了");

        } else if (e instanceof SystemBlockException) {
            r = Result.error(103,"触发系统保护规则了");

        } else if (e instanceof AuthorityException) {
            r = Result.error(104,"授权规则不通过");
        }
        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), r);

    }
}

2.4 系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

这些不是很好测试,我们可以此时一下QPS

  1. 代码

    @RestController
    public class SystemRuleController {
    
        @RequestMapping("/systemRule")
        public Result systemRule(){
            return Result.ok("测试系统规则QPS");
        }
    }
    
  2. 流控规则

    image.png

  3. jmeter测试

    image.png

  4. 查看监控图

    大于阈值之后进行限流

image.png

2.5 集群规则

https://github.com/alibaba/Sentinel/wiki/集群流控

为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。

另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果

部署方式

  • 独立模式(Alone),即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。

    image.png

  • 嵌入模式(Embedded),即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。

    image.png

相关文章:

  • QML 批量创建模块 【Repeater】 组件详解
  • k8s 1.30 安装ingress-nginx
  • 解决网络异常 repo sync 中断下载的问题
  • java 发送邮件功能
  • 吾爱置顶软件,吊打电脑自带功能!
  • 探索高通骁龙光线追踪技术
  • 视频设备轨迹回放平台EasyCVR打造视频智能融合新平台,驱动智慧机场迈向数字新时代
  • 【HTB】Windwos-easy-Legacy靶机渗透
  • 从零开始学习PX4源码19(飞行模式管理学习)
  • PyTorch嵌入层(nn.Embedding)
  • C++从入门到实战(十)类和对象(最终部分)static成员,内部类,匿名对象与对象拷贝时的编译器优化详解
  • LeetCode 891 -- 贡献度思想
  • 【爬虫】网易云音乐评论数据爬取
  • nodejs、socket.io、express + 实时线上聊天系统(自用笔记)
  • 若依——基于AI+若依框架的实战项目(实战篇(下))
  • 大模型中的参数规模与显卡匹配
  • forms实现俄罗斯方块
  • 什么是数据仓库
  • 从效率瓶颈到智能飞跃:AI工具如何重塑 Java开发模式
  • 敏捷开发:以人为本的高效开发模式