Sentinel持久化实战

前言

Sentinel有pull(拉)模式,和push(推)模式。本文是使用reids实现pull模式。

通过SPI机制引入自己的类

在项目的 resources > META-INF > services下创建新文件,文件名如下,内容是自己实现类的全限定名:com.xx.sentinel.RedisDataSourceInit
在这里插入图片描述

创建InitFunc实现类

package com.xx.sentinel;import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
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.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xx.schedule.Utils.SpringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;import java.util.List;/*** @Author: codeAgent <br>* @CreateDate: 2023/07/01 11:14 <br>* @Description: sentinel初始化方法,进行持久化设置*/
public class RedisDataSourceInit implements InitFunc {/*** sentinel存放redis的key。例如:sentinel:common:flow*/private static final String SENTINEL_REDIS_KEY = "sentinel:%s:%s";/*** sentinel数据更新发布订阅频道。例如:chanel_sentinel_common_flow*/private static final String SENTINEL_REDIS_CHANEL = "chanel_sentinel_%s_%s";private RedisTemplate redisTemplate;private static final String RULE_FLOW = "flow";private static final String RULE_DEGRADE = "degrade";/*** 给模板对象RedisTemplate赋值,并传出去*/private RedisTemplate<String, Object> getRedisTemplate() {if (redisTemplate == null) {synchronized (this) {if (redisTemplate == null) {redisTemplate = SpringUtil.getBean("functionDomainRedisTemplate");}}}return redisTemplate;}/*** 获取sentinel存放redis的key** @param ruleType* @return*/private String getSentinelRedisKey(String ruleType) {String projectName = SentinelRedisHelper.getProjectName();return String.format(SENTINEL_REDIS_KEY, projectName, ruleType);}/*** 获取sentinel数据更新发布订阅频道** @param ruleType* @return*/private String getSentinelRedisChanel(String ruleType) {String projectName = SentinelRedisHelper.getProjectName();return String.format(SENTINEL_REDIS_CHANEL, projectName, ruleType);}@Overridepublic void init() throws Exception {// 没有配置redis或没有配置projectName则不进行持久化配置if (getRedisTemplate() == null || StringUtils.isEmpty(SentinelRedisHelper.getProjectName())) {return;}// 1.处理流控规则this.dealFlowRules();// 2.处理熔断规则this.dealDegradeRules();}/*** 处理流控规则*/private void dealFlowRules() {String redisFlowKey = getSentinelRedisKey(RULE_FLOW);String redisFlowChanel = getSentinelRedisChanel(RULE_FLOW);// 注册flow读取规则// 官方RedisDataSource是订阅获取,官方FileRefreshableDataSource是定时刷新获取。本方法是redis订阅+定时Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});ReadableDataSource<String, List<FlowRule>> redisFlowDataSource = new RedisDataSource2<>(parser, getRedisTemplate(), redisFlowKey, redisFlowChanel);FlowRuleManager.register2Property(redisFlowDataSource.getProperty());// 初始化加载一次所有flow规则String flowRulesStr = (String) getRedisTemplate().opsForValue().get(redisFlowKey);List<FlowRule> flowRuleList = parser.convert(flowRulesStr);redisFlowDataSource.getProperty().updateValue(flowRuleList);// 注册flow写入规则。这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSource<List<FlowRule>> flowRuleWds = new RedisWritableDataSource<>(JSON::toJSONString, getRedisTemplate(), redisFlowKey, redisFlowChanel);WritableDataSourceRegistry.registerFlowDataSource(flowRuleWds);}/*** 处理熔断规则*/public void dealDegradeRules() {String redisDegradeKey = getSentinelRedisKey(RULE_DEGRADE);String redisDegradeChanel = getSentinelRedisChanel(RULE_DEGRADE);Converter<String, List<DegradeRule>> parser = source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {});ReadableDataSource<String, List<DegradeRule>> redisDegradeDataSource = new RedisDataSource2<>(parser, getRedisTemplate(), redisDegradeKey, redisDegradeChanel);DegradeRuleManager.register2Property(redisDegradeDataSource.getProperty());// 初始化加载一次所有flow规则String degradeRulesStr = (String) getRedisTemplate().opsForValue().get(redisDegradeKey);List<DegradeRule> degradeRuleList = parser.convert(degradeRulesStr);redisDegradeDataSource.getProperty().updateValue(degradeRuleList);// 注册degrade写入规则。这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.WritableDataSource<List<DegradeRule>> degradeRuleWds = new RedisWritableDataSource<>(JSON::toJSONString, getRedisTemplate(), redisDegradeKey, redisDegradeChanel);WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWds);}
}

创建读取redis类

继承AutoRefreshDataSource类,可以实现定时读取redis数据,防止redis发布订阅没收到时,更新不到新数据,相当于兜底方案。

package com.XX.sentinel;import com.alibaba.csp.sentinel.datasource.AutoRefreshDataSource;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;import java.nio.charset.StandardCharsets;/*** @Author: codeAgent <br>* @CreateDate: 2023/07/04 11:09 <br>* @Description: 订阅redis通知,当sentinel规则发生变化时,拉取redis配置保存到内存。定时获取redis信息*/
public class RedisDataSource2<T> extends AutoRefreshDataSource<String, T> {private static Logger logger = LoggerFactory.getLogger(RedisDataSource2.class);private static final String REDIS_SUCCESS_MSG = "OK";private String lastModified = "-1";private RedisTemplate redisTemplate;/*** 存入redis的对应规则的key*/private String ruleKey;/*** redis订阅频道*/private String channel;/*** 存入redis更新时间的key*/private String ruleUpdateKey;/*** 定时获取redis信息*/private static final long DEFAULT_REFRESH_MS = 300000L;public RedisDataSource2(Converter<String, T> parser, RedisTemplate redisTemplate, String ruleKey, String channel) {// 父级构造器,传入定时执行的时间super(parser, DEFAULT_REFRESH_MS);AssertUtil.notNull(redisTemplate, "redisTemplate can not be null");AssertUtil.notEmpty(ruleKey, "redis ruleKey can not be empty");AssertUtil.notEmpty(channel, "redis subscribe channel can not be empty");this.redisTemplate = redisTemplate;this.ruleKey = ruleKey;this.channel = channel;this.ruleUpdateKey = SentinelRedisHelper.getRedisUpdateTimeKey(ruleKey);subscribeFromChannel();}@Overridepublic String readSource() throws Exception {return (String) redisTemplate.opsForValue().get(ruleKey);}@Overridepublic void close() throws Exception {super.close();redisTemplate.execute((RedisCallback<String>) connection -> {connection.getSubscription().unsubscribe(channel.getBytes(StandardCharsets.UTF_8));return REDIS_SUCCESS_MSG;});}/*** 订阅消息队列*/private void subscribeFromChannel() {redisTemplate.execute((RedisCallback<String>) connection -> {connection.subscribe((message, pattern) -> {byte[] bytes = message.getBody();String msg = new String(bytes, StandardCharsets.UTF_8);logger.info("{},接收到sentinel规则更新消息: {} ", channel, msg);try {// 收到更新通知后,从redis获取全量数据更新到内存中getProperty().updateValue(parser.convert(readSource()));} catch (Exception e) {logger.error(channel + ",接收到sentinel规则更新消息:{},更新出错:{}", msg, e.getMessage());}}, channel.getBytes(StandardCharsets.UTF_8));return REDIS_SUCCESS_MSG;});}@Overrideprotected boolean isModified() {// 根据redis的key查询是否有更新,没有更新返回false,就不用执行后面的拉取数据,提高性能String updateTimeStr = (String) redisTemplate.opsForValue().get(ruleUpdateKey);if (StringUtils.isEmpty(updateTimeStr) || updateTimeStr.equals(lastModified)) {return false;}this.lastModified = updateTimeStr;return true;}}

创建写入redis实现类

package com.XX.sentinel;import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @Author: codeAgent <br>* @CreateDate: 2023/07/04 09:47 <br>* @Description: 收到sentinel控制的规则更新后,讲规则持久化,并发布redis订阅通知*/
public class RedisWritableDataSource<T> implements WritableDataSource<T> {private static Logger logger = LoggerFactory.getLogger(RedisWritableDataSource.class);private final String redisRuleKey;private final Converter<T, String> configEncoder;private final RedisTemplate redisTemplate;private final String redisFlowChanel;private final Lock lock;/*** 存入redis更新时间的key*/private String ruleUpdateKey;private static final String SENTINEL_RULE_CHANGE = "CHANGE";public RedisWritableDataSource(Converter<T, String> configEncoder, RedisTemplate redisTemplate, String redisRuleKey, String redisFlowChanel) {this.redisRuleKey = redisRuleKey;this.configEncoder = configEncoder;this.redisTemplate = redisTemplate;this.redisFlowChanel = redisFlowChanel;this.lock = new ReentrantLock(true);this.ruleUpdateKey = SentinelRedisHelper.getRedisUpdateTimeKey(redisRuleKey);}@Overridepublic void write(T value) throws Exception {this.lock.lock();try {logger.info("收到sentinel控制台规则写入信息,并准备持久化:{}", value);String convertResult = this.configEncoder.convert(value);redisTemplate.opsForValue().set(ruleUpdateKey, String.valueOf(System.currentTimeMillis()));redisTemplate.opsForValue().set(redisRuleKey, convertResult);logger.info("收到sentinel控制台规则写入信息,持久化后发布redis通知:{},信息:{}", this.redisFlowChanel, SENTINEL_RULE_CHANGE);redisTemplate.convertAndSend(this.redisFlowChanel, SENTINEL_RULE_CHANGE);} catch (Exception e) {logger.info("收到sentinel控制台规则写入信息,持久化出错:{}", e);throw e;} finally {this.lock.unlock();}}@Overridepublic void close() throws Exception {}
}

创建需要的配置类

package com.XX.sentinel;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/*** @Author: codeAgent <br>* @CreateDate: 2023/07/04 16:12 <br>* @Description: sentinel在redis持久化相关配置*/
@Component
public class SentinelRedisHelper {@Value("${project.name}")private String projectName;private static String SENTINEL_REDIS_UPDATE_TIME = "%s:updateTime";private static SentinelRedisHelper self;@PostConstructpublic void init() {self = this;}/*** 获取sentinel中配置的项目名** @return*/public static String getProjectName() {return self.projectName;}/*** 获取redis对应规则更新时间的key** @param redisKey redis对应规则的key* @return*/public static String getRedisUpdateTimeKey(String redisKey) {return String.format(SENTINEL_REDIS_UPDATE_TIME, redisKey);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/17709.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宝塔 安装/使用/备份数据 Jenkins-图文小白教程

一、Jenkins包下载 大家可以从Jenkins官网&#xff08;https://www.jenkins.io/&#xff09;根据自己的需要下载最新的版本。 但Jenkins官网下载较慢&#xff0c;容易造成下载失败。可以去国内的开源镜像网站下载Jenkins最新版本。目前博主使用的是清华大学的开源镜像网站&…

浅谈Node.js中的npm和yarn

官方文档&#xff1a; npmhttps://www.npmjs.cn/ yarnhttps://yarn.bootcss.com/ npm和yarn的作用 yarn和npm都是构建和打包javascript代码的工具 区别 npm&#xff1a; 1npm使得js开发者易于分享其为解决特定问题而编写的代码&#xff0c;且可供其他开发者在他们自己的应…

第十一章:C语言数据结构与算法初阶之排序

系列文章目录 文章目录 系列文章目录前言排序的概念及其运用排序的概念常见的排序算法 常见排序算法的实现1.直接插入排序2. 希尔排序&#xff08;缩小增量排序&#xff09;3. 直接选择排序4. 堆排序5. 冒泡排序6. 快速排序将区间按照基准值划分为左右两半部分的常见方式&#…

4 切割纸片

4 切割纸片 作者: 赵晓鹏时间限制: 1S章节: 动态规划与贪心 ---------------------------------输入 6 4 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 --------------------输出结果 4 #include <iostream> #include <vector> #include <climits&g…

Java 类概念简介

Java 是一种面向对象编程语言&#xff08;POO&#xff09;&#xff0c;它代表了封装、继承和多态性等概念。面向对象编程的功能是经典的&#xff0c;也是Java 对象创建的基础元素。在这篇文章中&#xff0c;我们讨论了 Java 中的类资源管理器&#xff0c;对创建者进行了评论&am…

TCP 协议(三)十种核心机制

1.确认应答&#xff08;可靠机制&#xff09; 2.超时重传&#xff08;可靠机制&#xff09; 3.连接管理&#xff08;可靠机制&#xff09; 4.滑动窗口&#xff08;效率机制&#xff09; 5.流量控制&#xff08;效率机制&#xff09; 6.拥塞控制&#xff08;效率机制&#xff09…

1 怎样用plsql developer导表数据

一 导少量表的情形 1.1 导出 点击‘工具’-‘导出表’&#xff1a; 选择要导出的用户和表&#xff0c;在’PL/SQL Developer’那里指定‘输出文件’&#xff0c;点击‘导出’按钮开始导出&#xff1a; 导出完毕后&#xff0c;最下方会提示‘正在导出表… 完成’。 --如果只想…

【Redis】2、Redis应用之【根据 Session 和 Redis 进行登录校验和发送短信验证码】

目录 一、基于 Session 实现登录(1) 发送短信验证码① 手机号格式后端校验② 生成短信验证码 (2) 短信验证码登录、注册(3) 登录验证① 通过 SpringMVC 定义拦截器② ThreadLocal (4) 集群 Session 不共享问题 二、基于 Redis 实现共享 session 登录(1) 登录之后&#xff0c;缓…

java版电子招标采购系统源码之电子招标采购实践与展望-招标采购管理系统

统一供应商门户 便捷动态、呈现丰富 供应商门户具备内外协同的能力&#xff0c;为外部供应商集中推送展示与其相关的所有采购业务信息&#xff08;历史合作、考察整改&#xff0c;绩效评价等&#xff09;&#xff0c;支持供应商信息的自助维护&#xff0c;实时风险自动提示。…

Prometheus - Concept

一 Prometheus 是什么 Prometheus 是一个开源的 监控和报警系统 。该系统内置和基于时间序列地抓取、存储、查询、绘图数据、报警。 现在是一个开源项目&#xff0c;继 K8S 后的第二个云原生计算基金会的托管项目&#xff0c;可见其火爆程度。 二 Prometheus 的特征 Promet…

Mysql (insert,update操作)

1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff0c;name&#xff08;员工名字&#xff09;&#xff0c;gender&#xff08;员工性别&#xff09;&#xff0c;salary&#xff08;员工薪资&#xff09; …

【网络编程】应用层协议——HTTP协议

文章目录 一、HTTP协议基本认识二、URL的认识2.1 urlencode和urldecode 三、HTTP协议格式3.1 HTTP请求与响应格式3.2 如何保证请求和响应被应用层完整读取&#xff1f;3.3 请求和响应如何做到序列化和反序列化&#xff1f;3.4 代码验证请求格式3.5 代码验证响应格式3.5.1 telne…