spring cloud sentinel 动态规则配置

Sentinel 允许在运行时根据不同的需求动态调整限流、熔断等规则。通过动态规则扩展,你可以实现:

  • 自动调整:根据业务流量的变化自动调整规则。
  • 外部配置支持:规则可以从数据库、配置中心(如 Nacos、Apollo)或者文件等外部来源加载。
  • 热更新:不需要重启应用,规则就能实时生效。

Sentinel 提供两种方式修改规则:

  • 通过 API 直接修改 (loadRules)
  • 通过 DataSource 适配不同数据源修改

通过 API 修改比较直观,可以通过以下几个 API 修改不同的规则:

复制代码
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则

手动修改规则(硬编码方式)一般仅用于测试和演示,生产上一般通过动态规则源的方式来动态管理规则。

DataSource 扩展

DataSource 扩展常见的实现方式有:

  • 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件。这样做的方式是简单,缺点是无法及时获取变更;
  • 推模式 :规则配置中心统一推送,客户端通过注册监听器的方式时刻监听变化,sentinel支持ZooKeeper, Redis, Nacos, Apollo, etcd等配置中心。这种方式有更好的实时性和一致性保证。

上述集中数据源扩展方式,sentinel是支持springboot自动装配的,下面以文件和nacos配置中心的形式来进行演示流量控制规则动态配置。

首先添加数据源扩展依赖

java 复制代码
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
文件模式

定义一个规则配置文件FlowRule.json放到resources下

json 复制代码
[
  {
    "resource": "add",
    "controlBehavior": 0,
    "count": 3.0,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0
  }
]

一个流量配置规则重要属性如下:

Field 说明 默认值
resource 资源名,资源名是限流规则的作用对象
count 限流阈值
grade 限流阈值类型,QPS 或线程数模式 QPS 模式
limitApp 流控针对的调用来源 default,代表不区分调用来源
strategy 调用关系限流策略:直接、链路、关联 根据资源本身(直接)
controlBehavior 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 直接拒绝

配置文件配置

yaml 复制代码
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080
      datasource:
        ds1:
          file:
            file: classpath:FlowRule.json #指定规则文件位置
            data-type: json #指定文件格式
            rule-type: flow #指定规则类型
            charset: utf-8 #指定文件编码

sentinel的datasource配置支持是Map<String, DataSourcePropertiesConfiguration>类型,可以支持同时配置多个数据源。不同的数据源有几个共同的配置项:

data-type: 数据格式类型,默认json

rule-type:规则类型,flow,grade,system等值可配置,具体项可查看RuleType枚举类。

converterClass:配置数据格式化处理类,默认json使用的jackson的ObjectMapper进行解析。

Nacos数据源

nacos添加额外依赖

复制代码
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

配置文件配置

yaml 复制代码
spring:
  cloud:
    sentinel:
      datasource:
        ds2:
          nacos:
            serverAddr: localhost:8848
            namespace: sentinel
            groupId: flowtest
            dataId: sentinel_system_flow_rule.json
            dataType: json
            ruleType: flow

nacos数据源配置和其作为配置中心信息差不多,nacos的连接信息,配置资源文件位置。

手动编码配置

除了使用springboot自动装配扩展数据源,也可以通过手动编码的方式进行自定义配置。使用FlowRuleManager.register2Property()方法进行手动注册数据源,例如手动注册一个文件数据源

java 复制代码
//读取配置文件内容
ClassLoader classLoader = getClass().getClassLoader();
String flowRulePath = URLDecoder.decode(classLoader.getResource("FlowRule.json").getFile(), "UTF-8");
//定义converter
Converter<String, List<FlowRule>> flowRuleListParser = s -> JSON.parseObject(s,new TypeReference<List<FlowRule>>(){});
//构造FileRefreshableDataSource
FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(
		flowRulePath, flowRuleListParser);
//注册数据源
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

手动注册数据源时候一定要注意代码执行顺序,否则执行过早可能在sentinel的控制台看不到配置规则信息,最好在容器初始化完成后执行,也可以使用sentinel自带的InitFunc的spiloader扩展方式。

动态规则扩展的原理

所有的动态数据源扩展最后都是通过FlowRuleManager.register2Property将数据源注册到规则管理器上。

FlowRuleManager.register2Property()

java 复制代码
    public static void register2Property(SentinelProperty<List<FlowRule>> property) {
        AssertUtil.notNull(property, "property cannot be null");
        synchronized (LISTENER) {
            RecordLog.info("[FlowRuleManager] Registering new property to flow rule manager");
            currentProperty.removeListener(LISTENER);
            property.addListener(LISTENER);
            currentProperty = property;
        }
    }

这里会给property添加一个listenner。这里以文件类型扩展源来看下。

先来看FileRefreshableDataSource内部动态刷新机制,其实很简单就是一个定时器检测文件变化。FileRefreshableDataSource继承抽象类AutoRefreshDataSource,其构造函数会启动定时器。

AutoRefreshDataSource#startTimerService()

java 复制代码
    private void startTimerService() {
      	//初始化线程池
        service = Executors.newScheduledThreadPool(1,
            new NamedThreadFactory("sentinel-datasource-auto-refresh-task", true));
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    //文件是否有变化
                    if (!isModified()) {
                        return;
                    }
                    //如果有变化从新从文件读取规则数据
                    T newValue = loadConfig();
                   //调用updateValue方法触发规则更新
                    getProperty().updateValue方法触发规则更新(newValue);
                } catch (Throwable e) {
                    RecordLog.info("loadConfig exception", e);
                }
            }
        }, recommendRefreshMs, recommendRefreshMs, TimeUnit.MILLISECONDS);
    }

这里定时周期默认在FileRefreshableDataSource有常量DEFAULT_REFRESH_MS=3000,3秒检测一次。

isModified()在FileRefreshableDataSource实现就是根据文件的修改时间来判断file.lastModified()。

最重要的updateValue()方法,这里的property实例是DynamicSentinelProperty类型,

DynamicSentinelProperty#updateValue()

java 复制代码
    public boolean updateValue(T newValue) {
        if (isEqual(value, newValue)) {
            return false;
        }
        RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);

        value = newValue;
        for (PropertyListener<T> listener : listeners) {
            listener.configUpdate(newValue);
        }
        return true;
    }

这里看到会拿出Property中所有的listener依次调用configUpdate方法。listener的设置在我们第一步注册数据源到FlowRuleManager里就设置了。这里listener的类型是FlowPropertyListener。

FlowRuleManager.FlowPropertyListener.configUpdate()

java 复制代码
public synchronized void configUpdate(List<FlowRule> value) {
  Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
  if (rules != null) {
  	flowRules = rules;
  }
  RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
}

最后将flowRule更新到内存中。

自动装配数据源原理

在sentinel的自动装配类SentinelAutoConfiguration中会初始化数据源处理类SentinelDataSourceHandler。该handler实现了SmartInitializingSingleton接口,在容器初始化完成后会调用afterSingletonsInstantiated()方法。

SentinelDataSourceHandler#afterSingletonsInstantiated

java 复制代码
	public void afterSingletonsInstantiated() {
		sentinelProperties.getDatasource()
          //循环处理所有配置的datasource
				.forEach((dataSourceName, dataSourceProperties) -> {
					try {
						List<String> validFields = dataSourceProperties.getValidField();
						if (validFields.size() != 1) {
							log.error("[Sentinel Starter] DataSource " + dataSourceName
									+ " multi datasource active and won't loaded: "
									+ dataSourceProperties.getValidField());
							return;
						}
						AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
								.getValidDataSourceProperties();
						abstractDataSourceProperties.setEnv(env);
						abstractDataSourceProperties.preCheck(dataSourceName);
                      //将解析验证后数据源信息作为一个bean注册到容器中
						registerBean(abstractDataSourceProperties, dataSourceName
								+ "-sentinel-" + validFields.get(0) + "-datasource");
					}
					catch (Exception e) {
						log.error("[Sentinel Starter] DataSource " + dataSourceName
								+ " build error: " + e.getMessage(), e);
					}
				});
	}

registerBean()方法

java 复制代码
	private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
			String dataSourceName) {
		BeanDefinitionBuilder builder = parseBeanDefinition(dataSourceProperties, dataSourceName);
		
		this.beanFactory.registerBeanDefinition(dataSourceName,
				builder.getBeanDefinition());
		// init in Spring
		AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
				.getBean(dataSourceName);

		// register property in RuleManager
		dataSourceProperties.postRegister(newDataSource);
	}

dataSourceProperties.postRegister()

java 复制代码
	public void postRegister(AbstractDataSource dataSource) {
		switch (this.getRuleType()) {
		case FLOW:
			FlowRuleManager.register2Property(dataSource.getProperty());
			break;
		case DEGRADE:
			DegradeRuleManager.register2Property(dataSource.getProperty());
			break;
			...
		}
	}

这里看到最后也是通过FlowRuleManager.register2Property()将数据源注册到规则管理器中。

相关推荐
星月昭铭34 分钟前
Spring AI集成Elasticsearch向量检索时filter过滤失效问题排查与解决方案
人工智能·spring boot·spring·elasticsearch·ai
yh云想2 小时前
《微服务SpringCloud架构实践指南:从Nacos到Gateway的全面解析》
spring cloud·nacos·gateway·openfeign·filter
巴厘猫2 小时前
拥抱智能时代:Spring AI:在Spring生态中构建AI应用——深度剖析与实践
java·spring
loop lee2 小时前
【Spring】一文了解SpringMVC的核心功能及工作流程,以及核心组件及注解
java·后端·spring
Resean02232 小时前
SpringMVC 6+源码分析(一)初始化流程
java·后端·spring·servlet·springmvc
Chase_______3 小时前
redis快速入门及使用
java·数据库·redis·学习·spring·缓存
柊二三6 小时前
关于项目的一些完善功能
java·数据库·后端·spring
小沛911 小时前
Spring AI Alibaba JManus:前后端异步消息回显机制深度解析
人工智能·spring·jmanus
paopaokaka_luck18 小时前
基于Spring Boot+Vue的吉他社团系统设计和实现(协同过滤算法)
java·vue.js·spring boot·后端·spring