Sentinel Redis 数据源(sentinel-datasource-redis)实战指南:基于 Pub/Sub 推送模式的规则动态更新 Sentinel Redis 数据源sentinel-datasource-redis实战指南基于 Pub/Sub 推送模式的规则动态更新【免费下载链接】SentinelA powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)项目地址: https://gitcode.com/gh_mirrors/sentine/SentinelSentinel 的sentinel-datasource-redis扩展基于 Redis 的发布/订阅Pub/Sub能力实现推送模式数据源规则变更通过 Channel 实时推送到应用内存同时以 Redis StringruleKey承载规则快照供启动时加载初始规则。本文以该模块为核心完整讲解依赖引入、RedisDataSource的创建与注册、Redis 事务一致性发布、RedisConnectionConfig的三种部署形态单机 / Sentinel / Cluster构建方式并结合仓库源码与测试用例剖析其底层实现原理帮助你搭建一套控制台改规则 → Redis 推送 → 应用秒级生效的动态流控体系。适用前提模块基于 Lettuce 作为 Redis 客户端需要JDK 1.8 及以上当前版本暂不支持 Redis Cluster 作为数据源后端README 明确说明Lettuce 事务发布示例中之所以出现 Cluster 代码仅用于说明发布方如何在集群中执行事务详见下文。一、模块定位与工作原理该模块位于 sentinel-extension/sentinel-datasource-redis实现的是一个只读ReadableDataSource其类继承关系为RedisDataSourceT继承AbstractDataSourceString, T见 RedisDataSource.java属于 sentinel-datasource-extension 抽象数据源体系的一员。从源码注释与实现RedisDataSource.java可以清晰看出它的工作流初始化加载构造时先从 Redis 的 String 类型 KeyruleKey读取初始规则快照解析后写入内存对应loadInitialConfig()与readSource()方法订阅监听随后通过 Lettuce 的 Pub/Sub 连接订阅指定channel当有新消息发布时由内部类DelegatingRedisPubSubListener在message()回调中调用parser.convert(message)解析并通过getProperty().updateValue(...)实时更新到内存RedisDataSource.java规则生效数据源持有的Property注册到FlowRuleManager等规则管理器后内存更新即触发规则热加载无需重启应用。这里需要特别注意ruleKey规则持久化 Key与channel订阅通道是两个不同的概念——前者是 String 类型的规则快照后者是 Pub/Sub 消息通道。为了保证两者数据一致发布方应当把写值与发消息放在同一个 Redis 事务中执行详见第三节。二、引入依赖与快速接入2.1 Maven 依赖在你的项目pom.xml中加入如下依赖dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-datasource-redis/artifactId versionx.y.z/version /dependency其中x.y.z请替换为当前使用的 Sentinel 版本。从模块自身的 pom.xml 可以看出它传递依赖了sentinel-datasource-extension与io.lettuce:lettuce-core当前仓库锁定版本为5.3.1.RELEASE因此无需额外手动引入 Lettuce。2.2 创建数据源并注册到规则管理器ReadableDataSourceString, ListFlowRule redisDataSource new RedisDataSourceListFlowRule(redisConnectionConfig, ruleKey, channel, flowConfigParser); FlowRuleManager.register2Property(redisDataSource.getProperty());三个关键入参说明参数含义redisConnectionConfig使用RedisConnectionConfig构建的 Redis 连接配置见第四节ruleKey规则持久化 KeyRedis String用于启动时加载初始规则channel订阅的发布/订阅通道规则变更时推送新值最后一个参数flowConfigParser是ConverterString, ListFlowRule类型的解析器负责把 Redis 中的字符串反序列化为规则对象。以 JSON 为例可以使用 Fastjson 构建ConverterString, ListFlowRule flowConfigParser source - JSON.parseObject(source, new TypeReferenceListFlowRule() {});这也是仓库测试 StandaloneRedisDataSourceTest.java 中的标准写法。同理可以为DegradeRuleManager、SystemRuleManager等规则管理器各创建一个数据源实例并注册对应 Property从而同时订阅多种规则类型。在构造RedisDataSource时源码会对三个入参做非空校验AssertUtil.notNull(connectionConfig, ...)、AssertUtil.notEmpty(ruleKey, ...)、AssertUtil.notEmpty(channel, ...)见 RedisDataSource.java传入空值会直接抛出异常。2.3 验证数据源生效的测试模式仓库为三种部署模式各提供了测试用例可作为接入时的参考模板StandaloneRedisDataSourceTest.java使用redis-mock内存模拟 Redis可独立运行覆盖了初始化加载、事务发布后实时接收testPubMsgAndReceiveSuccess等场景SentinelModeRedisDataSourceTest.java 与 ClusterModeRedisDataSourceTest.java均标注了Ignore需要你先搭建真实的 Redis Sentinel / Cluster 环境才能运行。三、规则发布用 Redis 事务保证值与消息一致RedisDataSource初始化时先从ruleKey读取初始规则之后只依赖channel上的消息推送。因此如果发布方只PUBLISH而不SET新启动的实例将拿不到规则反过来如果只SET而不PUBLISH已运行实例不会感知变更。为保证一致性发布方应通过 Redis 事务同时完成写入与发布官方推荐形式如下MULTI SET ruleKey value PUBLISH channel value EXEC3.1 单机Standalone发布示例Lettucepublic T void pushRules(ListT rules, ConverterListT, String encoder) { StatefulRedisPubSubConnectionString, String connection client.connectPubSub(); RedisPubSubCommandsString, String subCommands connection.sync(); String value encoder.convert(rules); subCommands.multi(); subCommands.set(ruleKey, value); subCommands.publish(ruleChannel, value); subCommands.exec(); }先通过encoder.convert(rules)把规则列表编码为字符串然后在MULTI与EXEC之间依次执行SET与PUBLISH保证快照与推送原子完成。3.2 Redis Cluster 发布示例Lettuce Cluster Client若你的规则发布端连接的是 Redis Cluster则需注意Redis Cluster 的MULTI/EXEC事务要求所有 Key 落在同一个 Hash Slot 上。源码注释指出Transaction can be handled in Redis Cluster when just using the same key——即只要SET与PUBLISH操作的是同一个 KeyruleKey就能在同一 Slot 内完成事务。官方给出的示例public T void pushRules(ListT rules, ConverterListT, String encoder) { RedisAdvancedClusterCommandsString, String subCommands client.connect().sync(); int slot SlotHash.getSlot(ruleKey); NodeSelectionString, String nodes subCommands.nodes((n)-n.hasSlot(slot)); RedisCommandsString, String commands nodes.commands(0); String value encoder.convert(rules); commands.multi(); commands.set(ruleKey, value); commands.publish(channel, value); commands.exec(); }这里先用SlotHash.getSlot(ruleKey)计算ruleKey所属 Slot再通过nodes((n) - n.hasSlot(slot))精确选中持有该 Slot 的节点进而在该节点上执行事务。注意这里的示例是**发布端写入方**的写法并不意味着当前数据源支持以 Cluster 作为订阅后端——如前文所述README 已明确说明currently we do not support Redis Cluster从 RedisDataSource.java 也能看到当redisClusters非空时会走RedisClusterClient分支创建客户端并建立 Cluster Pub/Sub 订阅但该能力处于实验阶段使用前需自行验证。仓库测试 ClusterModeRedisDataSourceTest.java 完整复现了上述基于 Slot 的集群事务发布过程可对照参考。四、构建 RedisConnectionConfig三种部署形态RedisConnectionConfig采用 Builder 模式见 RedisConnectionConfig.java同时提供静态工厂方法与实例方法两套入口。其内部定义了三个默认端口常量与一个默认超时常量值说明DEFAULT_REDIS_PORT6379单机模式默认端口DEFAULT_SENTINEL_PORT26379Sentinel 模式默认端口DEFAULT_CLUSTER_PORT6379Cluster 模式默认端口DEFAULT_TIMEOUT_MILLISECONDS60 * 1000默认命令超时 60 秒毫秒在build()时如果三种模式均未配置既没有 host也没有 Sentinel / Cluster 节点列表会抛出IllegalStateException提示One of the following must be provided Host, Socket, Cluster or SentinelRedisConnectionConfig.java。4.1 单机Standalone模式RedisConnectionConfig config RedisConnectionConfig.builder() .withHost(localhost) .withPort(6379) .withPassword(pwd) .withDataBase(2) .build();单机模式支持的常用配置项均可通过 Builder 方法设置withHost(String)/withPort(int)主机与端口端口合法范围 065535withPassword(String)/withPassword(char[])认证密码withDatabase(int)Redis 数据库编号仅单机 / 主从可用Sentinel 与 Cluster 模式不支持需为非负数withClientName(String)连接客户端名称对应 Lettuce URI 的 clientNamewithTimeout(long)同步命令执行超时时间单位为毫秒默认 60 秒withSslEnable(boolean)及配套的证书项见 4.4。对应的 URI 构建逻辑在 RedisDataSource.java 的getRedisStandaloneClient()中依次写入 host、port、database、ssl、timeout再按需追加密码与 clientName。4.2 Sentinel 模式RedisConnectionConfig config RedisConnectionConfig.builder() .withRedisSentinel(redisSentinelServer1, 5000) .withRedisSentinel(redisSentinelServer2, 5001) .withRedisSentinelMasterId(redisSentinelMasterId).build();要点说明可调用多次withRedisSentinel(host, port)添加多个 Sentinel 节点以实现 Sentinel 高可用必须通过withRedisSentinelMasterId(String)指定 Sentinel 监控的主节点master名称从源码 RedisConnectionConfig.java 可以看到withRedisSentinel会先断言host null即 Sentinel 模式与单机模式互斥再向redisSentinels列表追加节点静态工厂Builder.redisSentinel(host[, port[, masterId]])可一步创建缺省端口时使用DEFAULT_SENTINEL_PORT26379。客户端构建时RedisDataSource.java会为每个 Sentinel 节点调用withSentinel(host, port)并设置withSentinelMasterId、SSL 与超时。RedisConnectionConfigTest中的testRedisSentinelMoreThanOneServerSuccess、testRedisSentinelMoreThanOneDuplicateServerSuccess等用例验证了多节点配置的累积行为RedisConnectionConfigTest.java。4.3 Cluster 模式RedisConnectionConfig config RedisConnectionConfig.builder() .withRedisCluster(redisSentinelServer1, 5000) .withRedisCluster(redisSentinelServer2, 5001).build();同样支持多次调用withRedisCluster(host, port)添加多个节点缺省端口使用DEFAULT_CLUSTER_PORT6379。集群客户端构建RedisDataSource.java会把每个节点构造成RedisURI列表后交给RedisClusterClient.create(...)其中注释特别强调所有 Redis 节点必须使用相同密码。再次提醒README 明确说明当前版本尚不支持 Redis Cluster 作为数据源后端选择此模式前请先确认你所用 Sentinel 版本的兼容性声明。4.4 SSL 与证书配置源码级补充这是 README 未展开、但源码中确实支持的能力。当withSslEnable(true)开启 SSL 后可通过以下 Builder 方法配置证书对应 RedisDataSource.java 的initSslOptions()配置项说明withTrustedCertificatesPath(String)信任证书路径以.jks结尾视为 Java KeyStore 格式走truststore否则视为 PEM 格式走trustManagerwithTrustedCertificatesJksPassword(String)JKS 信任库密码withKeyCertChainFilePath(String)客户端证书链文件路径withKeyFilePath(String)客户端私钥文件路径以.jks结尾走keystore否则走keyManagerwithKeyFilePassword(String)私钥 / 密钥库密码对应的配置项测试可参考 RedisConnectionConfigTest.java。4.5 Builder 互斥与校验规则速查从 RedisConnectionConfig.java 源码可总结出以下约束withHost要求当前未添加任何 Sentinel 节点否则抛IllegalStateExceptionSentinels are non-empty. Cannot use in Sentinel mode.withPort要求先设置 host且端口在 065535withRedisSentinel/withRedisCluster要求当前未设置单机 hostCannot use with Redis mode.withDatabase要求数据库编号非负数据库仅单机 / 主从可用withTimeout、withPassword、withClientName、withSentinelMasterId均有对应的非空 / 非负断言。五、底层实现原理从构造到热更新的完整调用链最后把整个模块的运行链路串起来源码依据RedisDataSource.java构造器L81-L97校验connectionConfig/ruleKey/channel非空根据getRedisClusters().size()是否为 0 决定创建RedisClient单机 / Sentinel还是RedisClusterClient随后依次执行loadInitialConfig()与subscribeFromChannel(channel)初始加载L246-L256loadInitialConfig()调用loadConfig()→readSource()L258-L271单机分支通过redisClient.connect().sync().get(ruleKey)读取规则快照解析后getProperty().updateValue(newValue)写入内存若初始值为空会记录 WARN 日志提示检查数据源订阅监听L231-L244subscribeFromChannel()创建StatefulRedisPubSubConnection单机或StatefulRedisClusterPubSubConnectionCluster注册DelegatingRedisPubSubListener后sync().subscribe(channel)实时更新L283-L293收到消息后记录 INFO 日志[RedisDataSource] New property value received for channel {}: {}并执行getProperty().updateValue(parser.convert(message))资源释放L273-L281close()关闭客户端连接单机与 Cluster 分支分别调用shutdown()。因此一次完整的规则下发链路是规则管理端如控制台 / 自研平台→ Redis 事务SET PUBLISH→ 应用内 Lettuce Pub/Sub 收到消息 → parser 解析 → Property 更新 → 规则管理器热加载。整个过程无需重启应用也无需轮询 Redis这正是推送模式相比拉取模式如定时轮询文件 / 数据库的核心优势所在。六、实践建议与注意事项Key 与 Channel 的命名规划建议按规则类型规划ruleKey与channel例如流控规则sentinel.rules.flow.ruleKey/sentinel.rules.flow.channel与仓库测试一致降级、系统、热点参数规则可各自独立成对方便多数据源分别订阅务必使用事务发布只SET不PUBLISH会导致运行中实例不感知只PUBLISH不SET会导致重启后丢失规则。官方推荐的事务形式是唯一保证两者一致的标准姿势初始规则缺失时loadInitialConfig()只会记录 WARN 日志而不会抛出异常应用仍可启动但规则将为空需自行判断是否接受冷启动无规则的风险版本兼容模块基于 Lettuce 5.3.1.RELEASE、JDK 1.8 编译接入前请确认与你所用 Sentinel 核心版本匹配的扩展版本部署形态选择单机模式是官方最成熟、测试最完善的形态测试用redis-mock覆盖Sentinel 模式需要真实 Sentinel 环境验证Cluster 模式当前官方明确不支持谨慎评估后再使用。相关资源索引模块 READMEsentinel-extension/sentinel-datasource-redis/README.md核心实现RedisDataSource.java连接配置RedisConnectionConfig.java、RedisHostAndPort.java测试用例StandaloneRedisDataSourceTest.java、SentinelModeRedisDataSourceTest.java、ClusterModeRedisDataSourceTest.java、RedisConnectionConfigTest.java抽象数据源基类sentinel-datasource-extension【免费下载链接】SentinelA powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)项目地址: https://gitcode.com/gh_mirrors/sentine/Sentinel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考