Sentinel 控制台与 Nacos 实现配置的双向同步和持久化存储
引言
随着微服务架构的普及,服务间的调用关系变得更加复杂,流量控制和熔断机制成为保障系统稳定性的关键。Sentinel
是阿里巴巴开源的一款流量控制组件,它提供了丰富的流量控制手段以及实时监控功能。但在实际应用中,为了实现配置的动态调整和持久化,通常会结合配置中心来使用。本文将详细介绍如何利用 Nacos
作为配置中心,与 Sentinel
控制台一起,实现配置的双向同步与持久化存储。
规则管理及推送
该部分内容引自官方问答
一般来说,规则的推送有下面三种模式:
推送模式 | 说明 | 优点 | 缺点 |
---|---|---|---|
原始模式 | API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource ) | 简单,无任何依赖 | 不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 | 扩展写数据源(WritableDataSource ), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 | 简单,无任何依赖;规则持久化 | 不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 | 扩展读数据源(ReadableDataSource ),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 | 规则持久化;一致性;快速 | 引入第三方依赖 |
原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中:
这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境。
Pull模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry
中。以本地文件数据源为例:
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
String flowRulePath = "xxx";
ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
flowRulePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {})
);
// 将可读数据源注册至 FlowRuleManager.
FlowRuleManager.register2Property(ds.getProperty());
WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
// 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
// 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
WritableDataSourceRegistry.registerFlowDataSource(wds);
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:
首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。
这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。
Push模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
1. 环境准备
在开始之前,请确保已安装以下软件:
- Java 8 或更高版本
- Maven 或其他构建工具
- Nacos 服务端(版本推荐 2.3.0 或以上)
- Sentinel 控制台(版本推荐 1.8.6)
1.1 安装 Nacos
您可以从 Nacos GitHub 仓库 下载最新的发行版,或者使用 Docker 安装 Nacos。这里以下载安装为例:
1 wget https://github.com/alibaba/nacos/releases/download/2.3.0/nacos-server-2.3.0.zip
2 unzip nacos-server-2.3.0.zip
3 cd nacos/bin
4 ./startup.sh -m standalone
如果需要持久化nacos的配置,后续出单独文章解析
1.2 安装 Sentinel 控制台
您也可以从 Sentinel GitHub 仓库 下载1.8.6版本的zip源码包
2. 使用 Nacos 作为 Sentinel 的数据源
2.1 项目加载
解压后利用idea
开发工具打开该项目
2.2 配置 Nacos 服务器
编辑 application.properties
文件,添加 Nacos 服务器的相关配置:
# Sentinel 连接nacos配置
sentinel.nacos.username={usr}
sentinel.nacos.password={pwd}
sentinel.nacos.namespace={namespace}
sentinel.nacos.server-addr={ip}:{port}
2.3 配置 Sentinel 控制台
在 Sentinel
控制台中,通过配置面板将 Nacos 设置为数据源。这一步骤可以通过控制台的“数据源管理”功能完成。
2.3.1 修改 POM 文件
因项目的nacos
作为数据源的相关文件原来在测试包内,现在需要将 sentinel-datasource-nacos
包的 scope
注释掉,以便在 Sentinel
控制台中使用 Nacos
作为数据源。
<!-- 将以下依赖的 scope 注释掉 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.6</version>
<scope>test</scope>
</dependency>
2.3.2 复制 Nacos 相关类
复制test包下的 FlowRuleNacosProvider
、FlowRuleNacosPublisher
、NacosConfig
和 NacosConfigUtils
四个类到 main
目录下的 rule
包下。
- 初始位置:
- 目标位置:
2.3.3 配置 Nacos
新增NacosPropertiesConfiguration
配置类
package com.alibaba.csp.sentinel.dashboard.rule;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "sentinel.nacos")
@Configuration
public class NacosPropertiesConfiguration {
private String serverAddr;
private String dataId;
private String groupId;
private String namespace;
private String username;
private String password;
//自行补充get与set方法
}
修改 NacosConfig
类以支持自定义配置:
package com.alibaba.csp.sentinel.dashboard.rule;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Properties;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Configuration
public class NacosConfig {
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws Exception {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
properties.put(PropertyKeyConst.USERNAME, nacosPropertiesConfiguration.getUsername());
properties.put(PropertyKeyConst.PASSWORD, nacosPropertiesConfiguration.getPassword());
return ConfigFactory.createConfigService(properties);
}
}
修改 FlowRuleNacosPublisher
确认配置文件格式为JSON
package com.alibaba.csp.sentinel.dashboard.rule;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules), ConfigType.JSON.getType());
}
}
2.4 配置 V2 版本 Controller
调用 Nacos
提供的服务层。
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
3. 前端页面源码修改
3.1 添加 Nacos 接口并修改地址
- 文件路径:
src/main/webapp/resources/app/scripts/controllers/identity.js
- 操作: 将
FlowServiceV1
改为FlowServiceV2
,将/dashboard/flow/
改为/dashboard/v2/flow/
。
3.2 修改页面中的路由地址
- 文件路径:
src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
- 操作: 搜索
dashboard.flowV1
并定位到第 57 行,去掉V1
。 - 文件路径:
src/main/webapp/resources/app/views/flow_v2.html
- 操作: 注释掉回到单机页面的按钮。
4. 微服务引用
4.1 添加依赖
在微服务项目的 pom.xml
文件中添加以下依赖:
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
4.2 配置 Sentinel
在微服务的 application.yml
或 application.properties
文件中配置 Sentinel:
spring:
cloud:
sentinel:
transport:
dashboard: ${sentinel.host}:${sentinel.port}
datasource:
ds1:
nacos:
server-addr: ${nacos.host}:${nacos.port}
username: ${nacos.name}
password: ${nacos.pwd}
namespace: ${nacos.namespace}
group-id: ${nacos.group}
data-id: ${spring.application.name}-flow-rules
data-type: json
rule-type: flow
5. Nacos 中增加 Sentinel 持久化配置文件
在 Nacos 中增加一个配置文件,其文件后缀和组名需要与上面的配置相匹配。
6. 双向同步与持久化存储
一旦配置完成,Sentinel
控制台中的所有更改都会被同步到 Nacos
中,而 Nacos
中的任何更改也会被自动加载到 Sentinel
控制台。这意味着开发人员可以在 Nacos
中直接修改配置文件,而不必重新启动 Sentinel
控制台即可看到效果。
6.1 动态刷新
当 Nacos
中的配置发生变化时,Sentinel
控制台能够自动检测到这些变化并进行刷新。这种机制保证了配置的一致性和实时性。
6.2 持久化存储
由于所有的配置都保存在 Nacos
中,因此即使 Sentinel
控制台重启或崩溃,配置也不会丢失。这对于生产环境下的稳定性至关重要。
7. 测试效果
完成上述步骤后,您可以测试 Sentinel 控制台与 Nacos 之间的双向同步和持久化存储功能。
8. 结论
通过将 Sentinel
控制台与 Nacos
结合使用,我们可以轻松地实现配置的动态管理和持久化存储。这种方式不仅简化了配置管理流程,还提高了系统的稳定性和可维护性。对于需要频繁调整规则的大规模分布式系统来说,这是一种非常实用的解决方案。