Sentinel 规则持久化,基于Redis持久化【附带源码】

B站视频讲解

学习链接🔗

文章目录

  • 一、理论
  • 二、实践
    • 2-1、dashboard 请求Redis
      • 2-1-1、依赖、配置文件引入
      • 2-1-2、常量定义
      • 2-1-3、改写唯一id
      • 2-1-4、新Provider和Publisher
      • 2-1-5、改写V2
    • 2-2、应用服务改造
      • 2-2-1、依赖、配置文件引入
      • 2-2-2、注册监听器
  • 三、源码获取
    • 3-1、使用
    • 3-2、获取方式
  • 四、参考


上一篇讲Sentinel的时候,是用dashboard直接和服务进行交互,实时的新增/删除限流规则,所有的规则都存储在应用服务的内存中,每次重启服务之后再刷新dashboard就没有对应规则信息了。

这当然不是我们希望看到的,如果想要长久的保存规则,有且只有一个办法,那就是规则数据持久化。


一、理论


  1. 既然dashboard可以把规则推送到服务端,那服务端就可以拿到规则去持久化到硬盘上(文件、MySQL…),怎么说呢?这看起来不是个好办法,首先它很low,其次也不好解决分布式系统数据一致性的问题。
  2. 那换一种思路,dashboard先把规则推送给A,再由A把规则下发到各个具体的应用服务。这样A就相当于一种中心存储,解决了数据存储的问题,同时A实时下发给应用服务解决了数据一致性的问题。

这个A,官方给出了几种实现Nacos、ZooKeeper、Apollo、Redis。(理论上是可以自己去重写做到任何实现)


第一种方式就不推荐了,下面基于方式二来做实践,这里选用Redis来,主要是目前电脑只安装了Redis,原理是一样的。

下面是官方给出的图,要理解,是先把规则给到A,再由A去下发规则。(所以需要修改dashboard源码,让它先请求A)


想深入了解的可以参看下面的文档:

  1. https://github.com/alibaba/Sentinel/wiki/动态规则扩展
  2. https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel

二、实践


基于上面的分析,需要分两步改造

  1. 让dashboard把规则数据推送给Redis
  2. 应用服务接受Redis的下发(基于Redis的发布订阅功能)

注:会在Redis定义一个Key用来存储最终的规则数据,还会定义一个通道用来实时推送规则数据


2-1、dashboard 请求Redis


从Github下载dashboard源码:https://github.com/alibaba/Sentinel/releases

默认情况下,dashboard限流策略请求的是这个 v1 接口,官方还提供了一个 v2,这个v2就是持久化的接口,基于不同的持久化策略,只需要在 v2的版本里面替换对应的 DynamicRuleProvider、DynamicRulePublisher
在这里插入图片描述


2-1-1、依赖、配置文件引入


1、Redis-starter 引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>${spring.boot.version}</version>
</dependency>

2、配置文件修改

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0

3、Redis配置

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setEnableTransactionSupport(true);
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

2-1-2、常量定义


public final class Constants {
    
    // 最终规则是存储的key
    public static final String RULE_FLOW_PREFIX = "sentinel:rule:flow:xdx";

    // Redis的订阅发布功能,需要一个通道
    public static final String RULE_FLOW_CHANNEL_PREFIX = "sentinel:channel:flow:xdx";
    
    // 每一个规则都需要唯一id,基于Redis生成id
    public static final String RULE_FLOW_ID_KEY = "sentinel:id:flow:xdx";
}

2-1-3、改写唯一id


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisIdGenerator {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public long nextId(String key) {
        return redisTemplate.opsForValue().increment(key, 1);
    }
}

在这里插入图片描述


2-1-4、新Provider和Publisher


Provider的目的是读取Redis的内存数据

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;

import static com.alibaba.csp.sentinel.dashboard.xdx.Constants.RULE_FLOW_PREFIX;

@Component("flowRuleRedisProvider")
public class FlowRuleRedisProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    private final Logger logger = LoggerFactory.getLogger(FlowRuleRedisProvider.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        Assert.notNull(appName, "应用名称不能为空");
        logger.info("拉取redis流控规则开始: {}", appName);
        String key = RULE_FLOW_PREFIX;
        String ruleStr = (String)redisTemplate.opsForValue().get(key);
        if(StringUtils.isEmpty(ruleStr)) {
            return Collections.emptyList();
        }
        List<FlowRuleEntity> rules = JSON.parseArray(ruleStr, FlowRuleEntity.class);
        logger.info("拉取redis流控规则成功, 规则数量: {}", rules.size());
        return rules;
    }
}

每次规则变动,都把最新规则存到Redis里面去,并使用Redis通道发布

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.List;

import static com.alibaba.csp.sentinel.dashboard.xdx.Constants.RULE_FLOW_CHANNEL_PREFIX;
import static com.alibaba.csp.sentinel.dashboard.xdx.Constants.RULE_FLOW_PREFIX;

@Component("flowRuleRedisPublisher")
public class FlowRuleRedisPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    private final Logger logger = LoggerFactory.getLogger(FlowRuleRedisPublisher.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        Assert.notNull(app, "应用名称不能为空");
        Assert.notEmpty(rules, "策略规则不为空");
        logger.info("推送流控规则开始, 应用名: {}, 规则数量: {}", app, rules.size());
        String ruleKey = RULE_FLOW_PREFIX;
        String ruleStr = JSON.toJSONString(rules);
        // 数据存储
        redisTemplate.opsForValue().set(ruleKey, ruleStr);
      
        // 数据发布
        redisTemplate.convertAndSend(RULE_FLOW_CHANNEL_PREFIX, ruleStr);
    }
}

2-1-5、改写V2


  1. 刚刚说默认是请求V1版本,这里为了简单,直接把V1的@RequestMapping注释,把V2的@RequestMapping改成V1(可以改前端,让它请求到V2)
  2. 把新 V1的Publisher和Provider改成新的Redis版本
  3. 下图也是每个类的位置

在这里插入图片描述


2-2、应用服务改造


  1. https://github.com/alibaba/Sentinel/wiki/动态规则扩展
  2. Sentinel 官方已经做了Redis适配,使用起来也很简单了

2-2-1、依赖、配置文件引入


1、新增pom文件

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-redis</artifactId>
    <version>1.8.6</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置文件

spring:
  redis:
    host: 127.0.0.1
    port: 6379

3、Redis 配置文件(和上面一样)

@Configuration
public class ConfigRedis {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) 
    {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setEnableTransactionSupport(true);
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

2-2-2、注册监听器


直接在启动类里面改造就好了

@SpringBootApplication
public class App11 implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(App11.class, args);
    }


    public static final String RULE_FLOW_PREFIX = "sentinel:rule:flow:xdx";

    public static final String RULE_FLOW_CHANNEL_PREFIX = "sentinel:channel:flow:xdx";

    @Override
    public void run(ApplicationArguments args)  {
        Converter<String ,List<FlowRule>> parser = source -> {
            List<FlowRule> flowRules = new ArrayList<>();
            if (source != null) {
                String replace = source.replace("\\", "");
                String substring = replace.substring(1, replace.length() - 1);
                flowRules = JSON.parseArray(substring, FlowRule.class);
            }
            return flowRules;
        };
        RedisConnectionConfig config = RedisConnectionConfig.builder()
                .withHost("127.0.0.1")
                .withPort(6379)
                .build();
        ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<>(config, RULE_FLOW_PREFIX, RULE_FLOW_CHANNEL_PREFIX, parser);
        FlowRuleManager.register2Property(redisDataSource.getProperty());

        System.out.println("redis-sentinel-持久化开启");
    }
}

三、源码获取


3-1、使用


下面是修改好的sentinel-dashboard和对应的应用服务,只需要修改两个服务中的Redis连接地址就可以使用,如果你的Redis是 默认的127.0.0.1 和 6379 则无需修改。

在这里插入图片描述


3-2、获取方式


  1. 关注微信公众号:小道仙97
  2. 回复关键字:Sentinel_xdx97

四、参考


  • https://github.com/all4you/sentinel-tutorial/blob/master/sentinel-practice/sentinel-persistence-rules/sentinel-persistence-rules.md
  • https://sentinelguard.io/zh-cn/blog/use-sentinel-dashboard-in-production.html
  • https://github.com/alibaba/Sentinel/tree/master/sentinel-extension/sentinel-datasource-redis
  • https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95
  • https://github.com/alibaba/Sentinel/tree/master/sentinel-extension/sentinel-datasource-redis
  • https://www.jianshu.com/p/997a2255ff23
  • https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel
  • https://github.com/alibaba/Sentinel/wiki/Sentinel-%E6%8E%A7%E5%88%B6%E5%8F%B0%EF%BC%88%E9%9B%86%E7%BE%A4%E6%B5%81%E6%8E%A7%E7%AE%A1%E7%90%86%EF%BC%89#%E8%A7%84%E5%88%99%E9%85%8D%E7%BD%AE
  • https://blog.csdn.net/qq_42714869/article/details/94553378

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从0到1实现自助棋牌室系统:技术调研

前言 春节返乡之际&#xff0c;发现老家县城竟然开了近十家棋牌室。巧的是朋友也有意涉足&#xff0c;便咨询我自助棋牌室的软件投入成本。作为程序员的我&#xff0c;在思考了自助棋牌室背后的技术需求后&#xff0c;嗅到了一丝丝商机&#xff1a;何不自己开发一个自助棋牌室…

操作系统的运行机制

目录 一. 特权指令与非特权指令二. 中断和异常2.1. 内中断2.2 外中断 三. 系统调用 注:很多人习惯把Linux、Windows、MacOS的“小黑框”中使用的命令也称为“指令”&#xff0c;其实这是“交互式命令接口”&#xff0c;注意与本节的“指令”区别开。本节中的“指令”指二进制机…

jenkins实战(1)

一, Jenkins官网介绍: Jenkins 持续集成、持续部署 下载地址:Jenkins download and deployment 提供两种类型: LTS(长期版)和Weekly(最近一周的版本) 注: 必须是Java8及以上版本(官网针对这一点有做说明) 二, 安装 下载war包,java -jar XXX --httpPort8081 或 下载war包…

Linux:kubernetes(k8s)搭建mater节点(kubeadm,kubectl,kubelet)(2)

安装k8有多种方式如&#xff1a; minikube kubeadm 二进制安装 命令行工具 我这里就使用kubeadm进行安装 环境 3台centos7 master ip &#xff1a;192.168.113.120 2G运存 2内核 node1 ip &#xff1a;192.168.113.121 2G运存 2内核 node2 ip &#xff1a;192.168.1…

Myqsort:基于冒泡排序算法的C语言实现

我们将详细介绍一个基于冒泡排序算法的自定义排序函数——Mysqrt。该函数通过使用用户提供的比较函数进行元素间的比较&#xff0c;并结合swap交换函数对任意类型的数据进行排序。下面是对代码的逐行解析。 逻辑导图 代码实现 // 头文件 #include<stdio.h>// 定义比较函…

关于uniapp小程序的分包问题

开发uniapp小程序时&#xff0c;在打包上传代码时会出现超出2M的打包限制不能上传&#xff0c;那么我们该怎么做呢&#xff1f; 1.对于图片&#xff0c;将图片从后端服务取&#xff0c;尽量不要放在静态资源&#xff0c;图片体积会影响打包大小。 2.使用分包&#xff0c;tabb…

SSH教程

ssh 是远程连接的利器, 可以说凡是涉及到 linux 服务器, ssh 就是一个绕不开的话题. 本文作为一个教程, 尽可能详细的帮助读者设置 ssh, 并给出一些常用的 ssh 配置方法 (主要用于 linux 系统的远程登录和文件传输). 1. 简介 ssh 分为两个部分, sshd 服务端和 ssh 客户端. ssh…

2024-02学习笔记

1.当我们向Set集合中添加一个已经存在的元素时 当我们向Set集合中添加一个已经存在的元素时&#xff0c;Set集合会如何处理呢&#xff1f;实际上&#xff0c;Set集合不会将重复的元素添加到集合中。当我们向Set集合中添加一个元素时&#xff0c;Set集合会首先判断该元素是否已…

[C++]C++使用yolov9结合bytetrack实现目标追踪演示

【简介】 在C中实现YOLOv9的目标检测与ByteTrack的多目标追踪是一个相对复杂的过程&#xff0c;涉及到深度学习、计算机视觉和实时数据处理等多个领域。下面我将简单介绍这两个技术&#xff0c;并概述如何在C中实现它们。 YOLOv9&#xff08;You Only Look Once&#xff0c;版…

STL常见容器(map/multimap容器)---C++

STL常见容器目录&#xff1a; 8.map/ multimap容器8.1 map基本概念8.2 map构造和赋值8.3 map大小和交换8.4 map插入和删除8.5 map查找和统计8.6 map容器排序8.6.1 内置类型排序8.6.2 自定义类型排序8.6.3 自定义和内置类型混合排序 8.7 实例8.7.1 案例描述8.7.2 实现步骤 8.map…

Vue.js+SpringBoot开发高校实验室管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…

基于springboot+vue的人格障碍诊断系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

云服务器租用哪家好?不要忽视第3点

注册资本&#xff1a;5亿元 权威认证&#xff1a;中央网信办云服务安全审查DJCP网络安全等级保护ITSS工信部云计算服务能力评估CSA STAR认证管理体系认证信息安全安全管理体系认证可信云服务认证可信云云主机等级评估5星 PCI-DSS支付卡行业数据安全认证 租哪家云服务器比较好&…

关于拖拽功能

文章目录 写在前面自己手动实现拖拽的demo技术细节&#xff1a;Js中拖拽(拉)事件&#xff08;drag 和 drop&#xff09;浏览器兼容性拖拽Api的介绍拖拽流程1.dragstart事件2.dragenter事件3.dragover事件4.drop事件(必须要dragover事件触发)5.dragend事件MDN关于拖拽的解析 相关…

latex中\documentclass[preprint,review,12pt]{elsarticle}的详细解释

在LaTeX中&#xff0c;\documentclass 是一个命令&#xff0c;用于指定文档所使用的文档类。文档类定义了文档的总体结构、格式和样式。elsarticle 是一个常用的文档类&#xff0c;它主要用于在Elsevier出版的期刊上提交论文。 详细解释 \documentclass[preprint,review,12pt…

基于Spring Boot+Vue的论坛网站

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

ViewModel 原理

在现代Android应用开发中&#xff0c;ViewModel是架构组件库的一个关键部分&#xff0c;它在提高应用的稳定性和性能方面发挥着重要作用。在这篇文章中&#xff0c;我们将深入探讨ViewModel的工作原理和最佳实践。 ViewModel简介 ViewModel是Android Jetpack架构组件的一部分…

2024.03.03 健身打卡第 14 天

成功只有一个——按照自己的方式&#xff0c;去度过人生 2024.03.03 健身打卡第 14 天

[Vulnhub]靶场 Red

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 …