Spring-Cloud–sentinel的应用
1. Sentinel的概念
在SpringCloud中为了解决服务的雪崩问题,从而为了保护服务,诞生了sentinel的概念,sentinel从各个维度,多个方面,全方位的保护我们的微服务资源,常用的维度有:
- QPS(Query per Second): 每秒的访问数量
- 资源的熔断,降级
- 热点Key的访问设置
- 微服务资源的权限管理(黑,白名单)
2. Sentinel的组成
一部分:Dashboard 控制台
sentinel的控制台给与用户较好的界面去观察微服务的访问和调用情况,另外可以在控制台给与资源相应的操作
sentinel的控制台本身是一个springboot工程,可以直接使用 java -jar 的方式使用,不需要依赖其他容器,如tomcat
二部分: 服务方的简单http接口
使用sentinel 需要在微服务方依赖sentinel的 jar 包,然后进行一些配置,这样sentinel原本自带的一些接口就可以为我们的微服务工作,他所提供的一些接口可以给Dashboard 控制台数据支持
#接口概览
http://127.0.0.1:8720/api
#获取资源的metrics信息
http://127.0.0.1:8720/cnode?id=/jifen/{jifenId}
#获取流控规则接口
http://127.0.0.1:8720/getRules?type=flow
#设置规则接口
http://127.0.0.1:8720/setRules
3. Sentinel 的基本使用
思路: 使用sentinel非常简单,具体分两步走
- maven中的jar包依赖 ,依赖包: spring-cloud-starter-alibaba-sentinel
- resourse 中配置sentinel ,需要告知sentinel 的 Dashboard 的地址 , 端口
注意点:
- Dashboard的端口默认为8719,可以自增+1,为的是防止自己指定,而微服务太多的情况下端口号冲突
- Dashboard 默认是懒加载,可以通过设置饥饿加载,eager = true 来实现(此处与我们学习的ribbon一样,默认都是懒加载,都需要通过饥饿加载来实现运行即加载的效果)
- 代码实现sentinel 依赖:
<!-- sentinel 起步依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- yml 配置
spring:
cloud:
sentinel:
# 指定sentinel的端口和地址
transport:
port: 8721
dashboard: localhost:8888
# 开启饥饿加载
eager: true
- sentinel 控制台图例
4. Sentinel 的流控规则
sentinel 的流控规则主要分为QPS 和 线程数两大控制方向, 而其后的处理方式有: 直接,关联,链路三种方式,处理效果又分为:快速失败,Warm Up(冷启动),匀速排队
解释 :
- QPS (Query per second) : 即每秒的请求数量
- 线程数: 即服务中的并发量,值得注意的是,线程数就是并发量,而QPS不一定是并发量,如QPS为1000,而由于服务的耗时操作少,线程执行后归还到线程池的速度快,可能只需要10个进程即可完成,所以要明确QPS与线程数的区别
- 直接: 顾名思义,即是直接进行接下来的操作,是对被保护的服务进行操作
- 关联: 在关联资源触碰到阈值时对被保护资源进行操作
- 链路: 在多个调用中,针对某一条调用链路进行操作,如果触碰到阈值,则该条链路无法访问请求的资源
- 快速失败: 直接拒绝请求
- Warm Up: 冷启动,如果启动需要耗时操作时可以如此设置,防止在刚开始进行耗时操作时过多的访问压垮服务,而给与一定的时间进行预热,之后达到处理峰值
- 匀速排队: 是为了针对访问在不同的时间内,访问量不同,因此设置一个时间长度,来匀速的处理请求,实现削峰填谷,让程序平稳的进行,使用到了漏桶算法,值得注意的是该处理方式不支持QPS> 1000 的场景
QPS
每秒钟的请求数,QPS并不是服务的线程数,请求在被线程处理的时间会根据程序本身的耗时操作而决定,所以如果有大量的请求去访问一个耗时非常少的资源,可能只需要少量线程即可完成
QPS的直接失败
顾名思义: 访问某个资源超过阈值时,该资源被保护不让访问
QPS 的Warm Up
即是: 在服务启动时,对该资源直接进行流控操作,在某段预热时长(单位秒)内,慢慢达到设定的阈值,期间超出的部分直接拒绝,如此达到保护目标资源的目的
Warm Up 的起始值为设定阈值的 1/3(如设定300,则起始阈值为100)
QPS的匀速排队
在访问目标资源时,由于访问在不同时间段内的访问量不同,设置此操作
在设定的时间段内(单位:毫秒),处理 阈值 个数的请求 , 每一个请求的处理时间为: 设置的超时时间 / 阈值
QPS的关联失败
如果关联资源的QPS超过阈值,则被保护资源拒绝访问
注意点: 关联资源之间不一定就是调用关系,可能并没有关系,而设置之后是控制关联资源的QPS阈值来限制目标资源的访问权限
QPS的关联冷启动
同上,也是对关联资源在超过设定的QPS阈值时,对目标资源进行冷启动处理,初始阈值依然是设定阈值的 1/3
而后在预热时长内达到设定阈值,预热时间内超出阈值的请求直接快速失败
QPS的关联排队等待
关联资源在超过设定阈值时,对于目标资源进行匀速排队的操作
QPS的链路快速失败
-
首先要明确一个概念,就是什么是链路
-
machine-root / \ / \ Entrance1 Entrance2 / \ / \ DefaultNode(nodeA) DefaultNode(nodeA)
如图,针对同一个资源有两个入口,Entrance1 和Entrance2 ,他们都调用了同一个资源nodeA,那么这样就形成了一个链路,对此,我们可以设置入口资源为Entrance1 或 Entrance2 中的一个,表示在某一个入口的QPS值达到阈值之后,切断这条链路,使该入口无法被访问
QPS的链路Warm Up 和 匀速排队
与上述介绍的冷启动和匀速排队的策略相同,只是针对的是链路中的某一个入口达到QPS阈值之后的操作
线程数
官方解释
并发线程数控制
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
理解
- 何为线程数?
- 线程数就是服务中的并发量,就是目前在处理请求的线程的数量
- 线程数和QPS的区别?
- 线程数是并发量,而QPS就是每秒的访问量,两者不同
- 在服务中处理请求时从线程池中获取线程,处理请求,完成之后归还线程到线程池中
- 明确一点,线程处理请求的速度会根据服务所需的耗时操作来决定,可能很多的请求到达服务,而本身每一个请求的耗时非常少,在1秒内只需要少量的线程即可完成所有请求
线程数超过阈值的操作
与QPS的操作一样,流控模式分为:直接,关联,链路,操作方式为: 快速失败,Warm Up,匀速排队
5. Sentinel的熔断降级
官方解释
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
理解:
所谓熔断降级就是为了解决微服务之间调用后产生的异常问题,如A调用了B,而B是微信支付或其他第三方的接口,由于当时的网络原因导致B的响应时长增加,从而造成调用方A的线程池被占用,极端情况下,A的线程池被耗尽,整个服务雪崩
QPS是对请求的数量进行控制,而无法对于进入服务内的请求是否异常进行控制,熔断降级就是对进入服务之中的请求是否异常来进行控制,从而保护我们的服务
所以当某个资源的响应时间的线程超过设定线程的阈值时,对于其可以进行熔断降级处理,暂时得关闭访问,熔断器open,当熔断时长到达之后,熔断器进入half-open状态,允许一个请求进入,无论该请求是否正常都会返回,然后如果这个请求正常则熔断器close,如果这个请求异常则熔断器继续open,继续熔断,反复上述步骤
熔断降级 – 慢调用比例
最大 RT (Response Time) : 响应时间,就是在规定什么叫做 “慢” , 单个请求的响应时长大于我们设定的最大RT,那么这个请求就叫做 “慢请求”
比例阈值: 响应时间大于设定RT的线程占总请求数的比例,取值0.0~1.0之间
熔断时长: 达到阈值之后服务请求拒绝的时长(单位:秒)
最小请求数: 1秒钟内发出的最少需要的请求的总数量
- 解读: 如果一秒钟内发送了5个请求,而其中 比例阈值 * 最小请求数 的个数的请求超过了最大RT 的时间,那么目标资源就会被熔断降级,熔断时长单位为秒
断路器的工作流程:
- 达到熔断的阈值之后,断路器的状态为open
- 熔断时长内所有的请求都被拒绝,无法进入
- 熔断时长之后,断路器的状态变为half-open,此时允许一个请求进来
- 若该请求没有异常,则断路器的状态为close,之后回归正常
- 若该请求依然异常,则断路器再次变为open状态,所有请求无法进入,等待下一次熔断时长之后
熔断降级 – 异常比例
熔断降级 – 异常数
6. Sentinel 的权限管理
思路: 微服务之间的调用亦可存在权限,具体步骤如下
- 我们在调用方通过OpenFeign的拦截器来添加serviceName到请求头中,serviceName即是服务名
- 在被调用方通过拦截器来获取请求头中的serviceName
- 在控制台规定资源调用者是否在黑,白名单之中
- 之后sentinel会自动进行权限管理
1. 调用方,通过OpenFeign拦截器RequestInterceptor添加serviceName
import feign.RequestInterceptor; // 可以看到这个拦截器是feign提供的
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
/**
* 统一请求拦截器 实现RequestInterceptor接口 实现其中方法即可
*/
@Component
public class CustomerRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 向请求头内增加servicename
requestTemplate.header("servicename","cloud-order");
}
}
此后,该服务内的所有请求的请求头中都包含这个服务名
2. 被调用方,实现RequestOriginParser接口,从请求头中获取serviceName
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 再被调用方配置,解析请求头中的servicename,用于权限管理
*/
@Component
public class CustomerRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//从请求头中获取servicename这个约定好的key
String servicename = request.getHeader("servicename");
// 返回即可
return servicename;
}
}
3.在控制台内设置黑白名单,和调用方的服务名
7. Sentinel 的针对来源
Sentinel 中针对来源是流控中的设置,默认default是针对所有请求来源,而需要设置时需要输入的是服务名
步骤同上述的权限管理一致
8. Sentinel 提供的注解 – @SentinelResource
作用:
自定义资源的名称(controller中的方法即资源)
使用了该注解的资源如果发生流控和熔断,都不会走默认的sentinel异常处理,需要我们自定义进行异常处理
基本异常处理方式
步骤:
- 在主逻辑下另写一个备用逻辑,保证返回值和形参一致,如果是流控异常需要增加BlockException形参,如果是熔断降级需要加Throwable形参
- 主逻辑上加注解@SentinelResource,其中指定value为资源名,blockHandler指定备用逻辑的方法名
//主逻辑
@RequestMapping("test9")
@SentinelResource(value="test9",blockHandler="test9Handler")
public String test9(String name){
//todo ...
return "test9";
}
//备选逻辑
public String test9Handler(String name,BlockException exception){
return "系统繁忙,请稍后重试!!!";
}
优化:
- 大量的备选逻辑会使controller膨胀
- 另外写一个类,专门放备选逻辑,方法写为static静态方法
- @SentinelResource注解中增加 blockHandlerClass,指定类名
//核心主逻辑
@GetMapping("test-d")
@SentinelResource(value = "test-d",blockHandlerClass= CustomBlockHandler.class,blockHandler="testblockHandler")
public Map test(String name){
return new HashMap(){{
put("code","200");
put("msg","success");
}};
}
import com.alibaba.csp.sentinel.slots.block.BlockException;
import java.util.HashMap;import java.util.Map;
public class CustomBlockHandler {
//降级逻辑(备选逻辑),BlockException参数必须写
//方法定义为静态方法
public static Map testblockHandler(String name, BlockException exception){
// 可以拿到主逻辑中的name值
System.out.println(name);
return new HashMap(){{
put("code","200");
put("msg","系统繁忙,请稍后重试!!!!");
}};
}
}
最终方式
上述两种方式都会使代码膨胀,而增加了@SentinelResource注解后,实际上是sentinel将这个异常抛出给了DispatcherServlet,因此,我们可以采用Springboot的全局异常处理来解决这个异常问题,最终采用全局处理的方式进行处理,步骤如下:
- 使用springboot的全局异常处理,编写一个类,类上加注解@ControllerAdvice
- 返回值根据需要确定,可以与主逻辑不同,方法上增加注解@ExceptionHandler(注明异常类型)
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class CustomerExceptionHandler {
/**
* 单个异常处理
*/
public static String test04(String flag,BlockException e) {
System.out.println(flag);
if (e instanceof DegradeException){
return "熔断降级,您访问的页面丢失了,请稍后再试";
}
if (e instanceof FlowException){
return "流控异常,不能访问";
}
return "其他异常";
}
/**
* 全局流控异常处理
*/
@ExceptionHandler(FlowException.class)
@ResponseBody //别忘了
public static Map handlerFlowException() {
return new HashMap(){{
put("msg","全局流控异常处理");
}};
}
/**
* 全局熔断降级异常处理
*/
@ExceptionHandler(DegradeException.class)
@ResponseBody
public static Map handlerDegradeException() {
return new HashMap(){{
put("msg","全局熔断异常处理");
}};
}
/**
* 全局权限异常处理
*/
@ExceptionHandler(AuthorityException.class)
@ResponseBody
public static Map handlerAuthorityException() {
return new HashMap(){{
put("msg","全局权限异常处理");
}};
}
}
9. Sentinel 规则的持久化
9.1 原始模式
最基本的Sentinel持久化模式,即Sentinel 的 Dashboard 直接通过API 将更改后的规则发布到sentinel所在的微服务的内存中去
好处:
- 简单,方便,不需要任何 jar 包依赖
坏处:
- 硬编码,只适用于测试环境,在日后的开发环境中还是会使用动态持久化技术
9.2 Pull 模式
所谓的拉模式,实际上是在原始模式的基础上,保留了一份sentinel的规则到本地文件中,sentinel中的 FileRefreshableDataSource 会制定一个定时任务,定期的从本地文件中获取记录sentinel规则的相关JSON数据,同时其本身也会接收来自Sentinel Dashboard 推送的规则,将更新的规则更新到本地文件中
- Pull 模式所需要的依赖
<!-- sentinel Pull模式持久化依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
- 完成将下述代码,指定规则保存的地址和文件
package com.qf.sentinel;
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* 拉模式规则持久化
*/
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
String ruleDir = "D:/sentinel/rules";
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
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
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
- 文件内容为上个步骤中的类的全限定类名
Pull的优缺点
优点 :
- 没有多余的配置,仅需上述步骤即可(无需在yml中配置)
- 步骤简单,利于上手操作
缺点 :
- 由于FileRefreshableDataSource制定的是一个定时任务,那么其sentinel规则的更新必然会存在延迟性
- 规则保存在本地,若项目进行迁移,则需要将这些文件一起迁移,不符合我们的要求
9.3 Push 模式 – 最终选择方案
实际开发中应该选择push模式作为sentinel的规则数据源,常用的数据源有: Nacos,ZooKeeper,e.g. 等,数据源只做接收Sentinel Dashboard 推送的规则,而不从内存中获取,当发现推送规则发生改变,由数据源推送规则到Sentinel所在的微服务的内存中,实现实时更新
如图所示,Push 模式的工作流程如下:
- 我们在sentinel dashboard中指定的规则会推送push到Nacos 中,在Nacos中进行持久化
- Nacos 中一旦发现Sentinel 的规则发生了变化,就将更新的规则继续Push到Sentinel所在的微服务的内存中,实现数据的更新
使用步骤
-
- pom依赖
<!-- sentinel起步依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel的nacos持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
-
- 改造sentinel的dashboard
此处可以使用懒人包
-
- 在nacos的通用配置文件中增加如下配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: pro
group: pro-group
sentinel:
transport:
port: 8719
dashboard: localhost:8888
eager: true
web-context-unify: false
datasource:
flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-flow-rules
rule-type: flow
degrade:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-degrade-rules
rule-type: degrade
param-flow:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-param-rules
rule-type: param-flow
system:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-system-rules
rule-type: system
authority:
nacos:
server-addr: ${nacos.server-addr}
username: ${nacos.username}
password: ${nacos.password}
namespace: ${nacos.namespace}
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-authority-rules
rule-type: authority
nacos:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: sentinel