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

spring-cloud中很多使用的是Hystrix组件来进行限流的,现在我们这里使用阿里的sentinel来实现熔断限流功能。

sentinel简介

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

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方法。

https://blog.vchar.top/java/1615900200.html

Spring-Cloud-Alibaba之Sentinel的更多相关文章

  1. Spring Cloud Alibaba(10)---Sentinel控制台搭建+整合SpringCloudAlibaba

    上一篇博客讲了Sentinel一些概念性的东西 Spring Cloud Alibaba(9)---Sentinel概述 这篇博客主要讲 Sentinel控制台搭建,和 整合SpringCloudAl ...

  2. Spring Cloud Alibaba(11)---Sentinel+Nacos持久化

    Sentinel+Nacos持久化 有关Sentinel之前有写过两篇 Spring Cloud Alibaba(9)---Sentinel概述 Spring Cloud Alibaba(10)--- ...

  3. Spring Cloud Alibaba整合Sentinel

    Spring Cloud Alibaba 整合 Sentinel 一.需求 二.实现步骤 1.下载 sentinel dashboard 2.服务提供者和消费者引入sentinel依赖 3.配置控制台 ...

  4. Spring Cloud alibaba网关 sentinel zuul 四 限流熔断

    spring cloud alibaba 集成了 他内部开源的 Sentinel 熔断限流框架 Sentinel 介绍 官方网址 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentine ...

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

    Sentinel是什么 Sentinel的官方标题是:分布式系统的流量防卫兵.从名字上来看,很容易就能猜到它是用来作服务稳定性保障的.对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第 ...

  6. Spring Cloud Alibaba 之 Sentinel 限流规则和控制台实例

    这一节我们通过一个简单的实例,学习Sentinel的基本应用. 一.Sentinel 限流核心概念 在学习Sentinel的具体应用之前,我们先来了解一下Sentinel中两个核心的概念,资源和规则. ...

  7. Spring Cloud Alibaba整合Sentinel流控

    前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...

  8. Spring Cloud Alibaba:Sentinel实现熔断与限流

    一.什么是Sentinel Sentinel,中文翻译为哨兵,是为微服务提供流量控制.熔断降级的功能,它和Hystrix提供的功能一样,可以有效的解决微服务调用产生的“雪崩效应”,为微服务系统提供了稳 ...

  9. Spring Cloud Alibaba(9)---Sentinel概述

    Sentinel概述 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统自适应保护.热点防 ...

  10. Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则

    上一篇我们介绍了如何通过Nacos的配置功能来存储限流规则.Apollo是国内用户非常多的配置中心,所以,今天我们继续说说Spring Cloud Alibaba Sentinel中如何将流控规则存储 ...

随机推荐

  1. the import java.util cannot be resolve

    重新配置一下build path 的jre,如果不行的话就重新设置jre(在add library中installed JREs)

  2. nacos服务注册之服务器端Distro

    一致性协议算法Distro阿里自己的创的算法吧,网上能找到的资料很少.Distro用于处理ephemeral类型数据 Distro协议算法看代码大体流程是: nacos启动首先从其他远程节点同步全部数 ...

  3. js的基本数据类型与引用数据类型

    基本数据类型与引用数据类型 基本数据类型有五种 /* 基本数据类型有: - String - Number - Boolean - Null ** typeof null === 'object' 这 ...

  4. Android中Context样式分析

    目录 1.样式定义以及使用 1.1.默认样式 1.2.样式定义及使用 1.3.当前样式下attr属性的获取 1.4.属性集合的定义与获取 2.Activity中Theme的初始化流程 2.1.系统调用 ...

  5. 通过序列号Sequence零代码实现订单流水号

    序列号管理 本文通过产品编码和订单流水号介绍一下序列号(Sequence)在crudapi中的应用. 概要 序列号 MySQL数据库没有单独的Sequence,只支持自增长(increment)主键, ...

  6. postman接口测试之设置全局变量和设置环境变量和全局变量

    一.概念 1.环境变量 就是接口的域名或IP地址. 2.全局变量 就是一个作用域为整个postman的变量. 二.使用场景 1.环境变量 在测试的过程中,经常会频繁切换环境,本地环境验证.发布到测试环 ...

  7. 再探命令行传参之c与python

    继上一次java命令行传参 python sys模块包括了一组非常实用的服务,内含很多函数方法和变量,用来处理Python运行时配置以及资源,从而可以与前当程序之外的系统环境交互,如:python解释 ...

  8. JSP, EL, JSTL的使用

    JSP基础指令和语法 回顾 在Jsp页面: 只要是Java代码就会原封不动的输出, 如果是html代码,就会转义为 out.write("<html>\r\n") 这样 ...

  9. Spring Boot 轻量替代框架 Solon 的架构笔记

    Solon 是一个微型的Java开发框架.项目从2018年启动以来,参考过大量前人作品:历时两年,4000多次的commit:内核保持0.1m的身材,超高的跑分,良好的使用体验.支持:RPC.REST ...

  10. 怎么理解onStart可见但不可交互

    前言 今天朋友遇到一个面试题,分享给大家: onStart生命周期表示Activity可见,那为什么不能交互呢? 这个问题看似简单,但涉及到的面还是比较多的,比如Activity生命周期的理解,进程的 ...