Spring Cloud Alibaba之Sentinel实现熔断限流功能

微服务中为了防止某个服务出现问题,导致影响整个服务集群无法提供服务的情况,我们在系统访问量和业务量高起来了后非常有必要对服务进行熔断限流处理。 其中熔断即服务发生异常时能够更好的处理;限流是限制每个服务的资源(比如说访问量)。
spring-cloud中很多使用的是Hystrix组件来进行限流的,现在我们这里使用阿里的sentinel来实现熔断限流功能。

sentinel简介

这个在阿里云有企业级的商用版本 应用高可用服务 AHAS;现在有免费的入门级可以先体验下,之后再决定是否使用付费的专业版或者是自己搭建。

官方文档地址

Sentinel的github

本文示例代码

sentinel功能概述

  • 流量控制:将随机的请求调整为合适的形状。即限制请求数量;
  • 熔断降级:当检测到调用链路中某个资源出现不稳定的表现,如请求响应时间长或者异常比例升高的时候,则对此资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。 采用的手段:1.并发线程数的限制;2.通过响应时间进行降级
  • 系统负载保护:Sentinel提供系统维度的自适应保护能力。即在系统负载较高时,自动将流量转发到其它集群中的机器上去, 使系统的入口流量和系统的负载达到一个平衡,保护系统能力范围内处理最多的请求。

sentinel和Hystrix的区别

  • 两者的原则是一致的,都是当一个资源出现问题时,让其快速失败,不波及到其它服务。
  • Hystrix采用的是线程池隔离的方式,优点是做到了资源之间的隔离,缺点是增加了线程切换的成本
  • Sentinel采用的是通过并发线程的数量和响应时间来对资源限制。

Sentinel规则

Sentinel默认定义如下规则:

流控规则

通过QPS或并发线程数来做限制,里面的针对来源可以对某个微服务做限制,默认是都限制。

  • 流控模式:
  • 直接:接口达到限流条件,开启限流;
  • 关联:当关联的资源达到限流条件时,开启限流(适合做应用让步)
  • 链路:当从某个接口过来的资源达到限流条件时,开启限流(限制更细致)

关于配置规则:可以直接使用url地址来配置,也可以通过自定义名称来配置(需要在方法上添加@SentinelResource("order")注解才能达到效果,可以重复)

链路限流不生效的问题:由于sentinel基于filter开发的拦截使用的链路收敛的模式,因此需要设置关闭链路收敛使链路收敛能够生效,

spring:
   cloud:
     sentinel:
       filter:
         # 关闭链路收敛使链路收敛能够生效
         enabled: false

降级规则

当满足设置的条件,对服务进行降级。

根据平均响应时间:当资源的平均响应时间超过阀值(以ms为单位)之后,资源进入准降级状态。
如果接下来1秒持续进入的n个请求的RT都持续超过这个阀值,则在接下来的时间窗口(单位s)之内就会使这个方法进行服务降级。

注意Sentinel默认的最大时间为4900ms,超过这个时间将被默认设置为4900ms;可以通过启动配置 -Dcsp.sentinel.statistic.max.rt=xxx来修改。

异常降级:通过设置异常数或者异常比例来进行服务降级。

热点规则

必须使用@SentinelResource("order")注解来做标记,将限流做到参数级别上去,并且可以配置排除参数值等于某个值时不做限流。

授权规则

通过配置黑白名单来设置是否允许通过。

自定义来源获取规则:

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
* <p> sentinel自定义授权来源获取规则 </p>
*/
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {

  /**
   * 定义区分来源的规则:本质上是通过获取request域中获取来源标识,然后交给流控应用来进行匹配处理
   *
   * @param request request域
   * @return 返回区分来源的值
   */
  @Override
  public String parseOrigin(HttpServletRequest request) {
      String client = request.getHeader("client");
      if(StringUtils.isNotBlank(client)){
          return "NONE";
      }
      return client;
  }
}

系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体Load、RT、入口QPS、CPU使用率和线程数五个维度来监控整个应用数据,让系统跑到最大吞吐量的同时保证系统稳定性。

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
  • CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。

sentinel的使用

下面我们通过一些简单的示例来快速了解sentinel的使用。

安装控制台界面工具

在Sentinel的Github上下载安装包https://github.com/alibaba/Sentinel/releases;就是一个jar包直接使用命令启动即可。

java -Dserver.port=9080 -Dcsp.sentinel.dashboard.server=localhost:9080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

-Dserver.port 是设置访问的端口号;
sentinel-dashboard.jar 就是刚刚下载的jar包名称;
为方便使用可以创建一个bat启动文件,在里面输入上面的命令行,后面启动直接点击这个bat文件即可。

从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel;启动成功后浏览器输入http://127.0.0.1:9080 即可访问控制台。
注意这个控制台不是必须接入的,同时只有你的接口方法被访问过后控制台里面才会显示。

服务中使用

添加如下依赖包

<!--由于我们使用的spring-cloud,因此这里因此 sentinel的集成包来简化我们的配置 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--sentinel 对dubbo的支持-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-apache-dubbo-adapter</artifactId>
</dependency>

注意如果没有使用dubbo那么无需引入sentinel-apache-dubbo-adapter; 比如之前使用的是feign和Hystrix搭配的,只需要将Hystrix的相关配置和依赖去掉,然后加入sentinel的依赖即可。

代码中的使用示例1,如果我们只需对相关的http方法进行限流,直接引入依赖的包即可;下面是我们向对某个方法进行限流,因此使用使用@SentinelResource注解来配置。

@Service
public class SentinelDemoServiceImpl implements SentinelDemoService {

    /**
     * sentinel 熔断限流示例1
     */
    @SentinelResource(value = "SentinelDemoService#sentinelDemo1", defaultFallback = "sentinelDemo1Fallback")
    @Override
    public String sentinelDemo1() {
        return "sentinel 示例1";
    }

    /**
     * 失败的时候会调用此方法
     */
    public String sentinelDemo1Fallback(Throwable t) {
        if (BlockException.isBlockException(t)) {
            return "Blocked by Sentinel: " + t.getClass().getSimpleName();
        }
        return "Oops, failed: " + t.getClass().getCanonicalName();
    }

}

然后在控制台配置相关的策略规则即可。

自定义Sentinel的异常返回

通过实现BlockExceptionHandler接口来自定义异常返回;注意之前的UrlBlockHandler 视乎已经在新版中移除了。

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {

    /**
     * 异常处理
     *
     * @param request  请求
     * @param response 响应
     * @param e        BlockException异常接口,包含Sentinel的五个异常
     *                 FlowException  限流异常
     *                 DegradeException  降级异常
     *                 ParamFlowException  参数限流异常
     *                 AuthorityException  授权异常
     *                 SystemBlockException  系统负载异常
     * @throws IOException IO异常
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        JSONObject responseData = new JSONObject();
        if (e instanceof FlowException) {
            responseData.put("message", "限流异常");
            responseData.put("code", "C5001");
        } else if (e instanceof DegradeException) {
            responseData.put("message", "降级异常");
            responseData.put("code", "C5002");
        } else if (e instanceof ParamFlowException) {
            responseData.put("message", "参数限流异常");
            responseData.put("code", "C5003");
        } else if (e instanceof AuthorityException) {
            responseData.put("message", "授权异常");
            responseData.put("code", "C5004");
        } else if (e instanceof SystemBlockException) {
            responseData.put("message", "系统负载异常");
            responseData.put("code", "C5005");
        }
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(responseData.toJSONString());
    }
}

基于文件实现Sentinel规则的持久化

Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。

编写一个实现InitFunc接口的类,在里面定义持久化的方式,这里使用文件

public class FilePersistence implements InitFunc {

  @Value("spring.application.name")
  private String applicationName;

  @Override
  public void init() throws Exception {
      String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + applicationName;
      String flowRulePath = ruleDir + "/flow-rule.json";
      String degradeRulePath = ruleDir + "/degrade-rule.json";
      String systemRulePath = ruleDir + "/system-rule.json";
      String authorityRulePath = ruleDir + "/authority-rule.json";
      String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

      this.mkdirIfNotExits(ruleDir);
      this.createFileIfNotExits(flowRulePath);
      this.createFileIfNotExits(degradeRulePath);
      this.createFileIfNotExits(systemRulePath);
      this.createFileIfNotExits(authorityRulePath);
      this.createFileIfNotExits(paramFlowRulePath);

      // 流控规则
      ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
              flowRulePath,
              flowRuleListParser
      );
      FlowRuleManager.register2Property(flowRuleRDS.getProperty());
      WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
              flowRulePath,
              this::encodeJson
      );
      WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

      // 降级规则
      ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
              degradeRulePath,
              degradeRuleListParser
      );
      DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
      WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
              degradeRulePath,
              this::encodeJson
      );
      WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

      // 系统规则
      ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
              systemRulePath,
              systemRuleListParser
      );
      SystemRuleManager.register2Property(systemRuleRDS.getProperty());
      WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
              systemRulePath,
              this::encodeJson
      );
      WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

      // 授权规则
      ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
              authorityRulePath,
              authorityRuleListParser
      );
      AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
      WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
              authorityRulePath,
              this::encodeJson
      );
      WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

      // 热点参数规则
      ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
              paramFlowRulePath,
              paramFlowRuleListParser
      );
      ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
      WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
              paramFlowRulePath,
              this::encodeJson
      );
      ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
  }

  private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
          source,
          new TypeReference<List<FlowRule>>() {
          }
  );
  private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
          source,
          new TypeReference<List<DegradeRule>>() {
          }
  );
  private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
          source,
          new TypeReference<List<SystemRule>>() {
          }
  );

  private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
          source,
          new TypeReference<List<AuthorityRule>>() {
          }
  );

  private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
          source,
          new TypeReference<List<ParamFlowRule>>() {
          }
  );

  private void mkdirIfNotExits(String filePath) throws IOException {
      File file = new File(filePath);
      if (!file.exists()) {
          file.mkdirs();
      }
  }

  private void createFileIfNotExits(String filePath) throws IOException {
      File file = new File(filePath);
      if (!file.exists()) {
          file.createNewFile();
      }
  }

  private <T> String encodeJson(T t) {
      return JSON.toJSONString(t);
  }
}

在resources下创建配置目录META-INF/services,然后添加文件 com.alibaba.csp.sentinel.init.InitFunc;在文件中添加上面写的配置类的全路径top.vchar.order.config.FilePersistence

使用Nacos实现动态规则配置

动态规则扩展文档

添加如下依赖

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

添加如下配置(具体可以参考SentinelProperties 配置类):

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          # 配置nacos的
          nacos:
            rule-type: FLOW
            server-addr: 127.0.0.1:8848
            namespace: public
            groupId: "DEFAULT_GROUP"
            dataId: dubbo-customer-demo-sentinel.rule
            username: nacos
            password: 123456

然后在nacos中创建一个配置文件 dubbo-customer-demo-sentinel.rule,类型为text; 具体配置参数见官网说明;下面是一个示例:

[
    {
        "resource": "SentinelDemoService#sentinelDemo2",
        "count": 0,
        "grade": 1,
        "limitApp":"default",
        "strategy":0,
        "controlBehavior":0,
        "clusterMode":false
    }
]

实际使用不建议这样做,还是建议使用控制台的方式;因为使用官方提供的集成方式时,nacos的时候会疯狂的拉取数据,同时只支持一个规则的配置;因此要么自己去基于nacos实现,要么使用控制台的方式;
且配置项很多,因此还是建议使用控制台的方式来实现,或者是对接其rest api接口,在实际操作中还是建议使用界面化的操作。

关于熔断降级是如何实现自动调用我们配置的Fallback方法

sentinel使用了spring的AOP切面编程功能拦截有@SentinelResource注解的类,具体查看com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect类,在执行实际的方法时使用try-catch进行异常捕获,
如果异常是BlockException的时候会调用handleBlockException方法(注意我们也可以配置自己自定义的异常也走这个方法),通过反射执行配置的Fallback方法。

到此这篇关于Spring Cloud Alibaba之Sentinel的文章就介绍到这了,更多相关Spring Cloud Alibaba内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringCloud-Alibaba-Sentinel服务降级,热点限流,服务熔断

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

  • Spring Cloud Alibaba使用Sentinel实现接口限流

    最近管点闲事浪费了不少时间,感谢网友 libinwalan 的留言提醒.及时纠正路线,继续跟大家一起学习Spring Cloud Alibaba. Nacos作为注册中心和配置中心的基础教程,到这里先告一段落,后续与其他结合的内容等讲到的时候再一起拿出来说,不然内容会有点跳跃.接下来我们就来一起学习一下Spring Cloud Alibaba下的另外一个重要组件:Sentinel. Sentinel是什么 Sentinel的官方标题是:分布式系统的流量防卫兵.从名字上来看,很容易就能猜到它是用来

  • Spring Cloud Alibaba教程之Sentinel的使用

    什么是Sentinel Sentinel,中文翻译为哨兵,是为微服务提供流量控制.熔断降级的功能,它和Hystrix提供的功能一样,可以有效的解决微服务调用产生的"雪崩"效应,为微服务系统提供了稳定性的解决方案.随着Hytrxi进入了维护期,不再提供新功能,Sentinel是一个不错的替代方案.通常情况,Hystrix采用线程池对服务的调用进行隔离,Sentinel才用了用户线程对接口进行隔离,二者相比,Hystrxi是服务级别的隔离,Sentinel提供了接口级别的隔离,Sentin

  • Spring Cloud Alibaba整合Sentinel的实现步骤

    一.需求 实现一个简单的 整合 sentinel,不涉及sentinel的用法 二.实现步骤 1.下载 sentinel dashboard https://github.com/alibaba/Sentinel/releases 注意: 默认会启动8080端口,如果端口冲突,可以在启动命令上加入 -Dserver.port=新端口 默认用户名和密码[sentinel/sentinel] 启动控制台可用的配置项 2.服务提供者和消费者引入sentinel依赖 <dependency> <

  • Spring Cloud Alibaba 使用 Feign+Sentinel 完成熔断的示例

    Feign的使用 Feign也是网飞开发的,SpringCloud 使用 Feign 非常简单,我下边演示一下: 首先 服务消费者这边肯定需要一个对应的依赖: compile("org.springframework.cloud:spring-cloud-starter-openfeign") 需要启用Feign的话,也得在启动类上面加个注解 @EnableFeignClients 然后,创建一个 Feign 的接口,像这样子 package com.skypyb.sc.feign;

  • SpringCloud-Alibaba-Sentinel-配置持久化策略详解

    前言: Sentinel的如果没有配置持久化的话配置一些 流控 和服务降级 从启项目就会置空所以需要持久化的操作 动态规则扩展 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS.文件,甚至是 VCS 等.这样做的方式是简单,缺点是无法及时获取变更: 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos.Zookeeper 等配置中心.这种方式有更好的实时性和一致性保证 案例用Nacos 步奏: pom 添加nacos 对sen

  • Spring Cloud Alibaba之Sentinel实现熔断限流功能

    微服务中为了防止某个服务出现问题,导致影响整个服务集群无法提供服务的情况,我们在系统访问量和业务量高起来了后非常有必要对服务进行熔断限流处理. 其中熔断即服务发生异常时能够更好的处理:限流是限制每个服务的资源(比如说访问量). spring-cloud中很多使用的是Hystrix组件来进行限流的,现在我们这里使用阿里的sentinel来实现熔断限流功能. sentinel简介 这个在阿里云有企业级的商用版本 应用高可用服务 AHAS:现在有免费的入门级可以先体验下,之后再决定是否使用付费的专业版

  • spring cloud gateway整合sentinel实现网关限流

    这篇文章主要介绍了spring cloud gateway整合sentinel实现网关限流,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 说明: sentinel可以作为各微服务的限流,也可以作为gateway网关的限流组件. spring cloud gateway有限流功能,但此处用sentinel来作为替待. 说明:sentinel流控可以放在gateway网关端,也可以放在各微服务端. 1,以父工程为基础,创建子工程 2,添加pom依赖

  • 浅析Spring Cloud Gateway中的令牌桶限流算法

    目录 前言 回顾限流算法 计数器/时间窗口法 漏桶法 令牌桶法 主要逻辑分析 前言 在一个分布式高并发的系统设计中,限流是一个不可忽视的功能点.如果不对系统进行有效的流量访问限制,在双十一和抢票这种流量洪峰的场景下,很容易就会把我们的系统打垮.而作为系统服务的卫兵的网关组件,作为系统服务的统一入口,更需要考虑流量的限制,直接在网关层阻断流量比在各个系统中实现更合适.Spring Cloud Gateway的实现中,就提供了限流的功能,下面主要分析下Spring Cloud Gateway中是如何

  • Spring Cloud Alibaba微服务组件Sentinel实现熔断限流

    目录 Sentinel简介 Sentinel具有如下特性: 安装Sentinel控制台 创建sentinel-service模块 限流功能 创建RateLimitController类 根据URL限流 自定义限流处理逻辑 熔断功能 与Feign结合使用 使用Nacos存储规则 原理示意图 功能演示 Sentinel简介 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Sentinel 作为其核心组件之一,具有熔断与限流等一系列服务保护功能,本文将对其用法进行详细介

  • 微服务间调用Retrofit在Spring Cloud Alibaba中的使用

    目录 前置知识 搭建 使用 集成与配置 服务间调用 服务限流 熔断降级 总结 前置知识 在微服务项目中,如果我们想实现服务间调用,一般会选择Feign.之前介绍过一款HTTP客户端工具Retrofit,配合SpringBoot非常好用!其实Retrofit不仅支持普通的HTTP调用,还能支持微服务间的调用,负载均衡和熔断限流都能实现.今天我们来介绍下Retrofit在Spring Cloud Alibaba下的使用,希望对大家有所帮助! SpringBoot实战电商项目mall(50k+star

  • 简单了解Spring Cloud Alibaba相关知识

    这篇文章主要介绍了简单了解Spring Cloud Alibaba相关知识,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 官方github地址 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务. 主要功能 服务限流降级:默认支持 WebServlet.WebFlux, OpenFeign

随机推荐