当前位置: 首页 > news >正文

RocketMQ源码 Broker-ConsumerFilterManager 消费者数据过滤管理组件源码分析

前言

ConsumerFilterManager 继承了ConfigManager配置管理组件,拥有将内存数据持久化到磁盘文件consumerFilter.json的能力。它主要负责,对在消费者拉取消息时,进行消息数据过滤,且只针对使用表达式过滤的消费者有效。


源码版本:4.9.3

源码架构图

核心数据结构

可以看到内存中维护了 topic -> consumer group -> ConsumerFilterData 映射关系的数据结构。

/*** Consumer filter data manager.Just manage the consumers use expression filter.* 消费者过滤数据管理组件。只管理使用表达式过滤的消费者。*/
public class ConsumerFilterManager extends ConfigManager {// 核心数据结构:topic -> consumer group -> ConsumerFilterDataprivate ConcurrentMap<String/*Topic*/, FilterDataMapByTopic>filterDataByTopic = new ConcurrentHashMap<String/*Topic*/, FilterDataMapByTopic>(256);private transient BrokerController brokerController;// 布隆过滤器private transient BloomFilter bloomFilter;
}

深入看下 FilterDataMapByTopic 类,是上面数据结构的一个子集,维护了 消费组 -> 消费组过滤数据映射关系。

    public static class FilterDataMapByTopic {// 核心数据结构:consumer group -> ConsumerFilterDataprivate ConcurrentMap<String/*consumer group*/, ConsumerFilterData>groupFilterData = new ConcurrentHashMap<String, ConsumerFilterData>();private String topic;}

在深入一步,看下 ConsumerFilterData 的数据结构,包含了全部与消费者过滤有关的关键信息。

/*** Filter data of consumer.*/
public class ConsumerFilterData {// 消费组private String consumerGroup;// 主题private String topic;// 过滤器表达式private String expression;// 过滤器类型private String expressionType;// 过滤器编译后的表达式private transient Expression compiledExpression;// 过滤器创建时间private long bornTime;// 过滤器过期时间private long deadTime = 0;// 过滤器版本private long version;// 布隆过滤器数据private BloomFilterData bloomFilterData;// 客户端版本private long clientVersion;
}

核心数据行为

从下面代码可以看到,ConsumerFilterManager的行为主要是注册订阅、取消订阅、清理过期订阅、序列化、反序列化等维护内存元数据的行为。过滤行为不在这个组件里体现,在其他调用方法中会有具体使用方式。

/*** Consumer filter data manager.Just manage the consumers use expression filter.* 消费者过滤数据管理组件。只管理使用表达式过滤的消费者。*/
public class ConsumerFilterManager extends ConfigManager {private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.FILTER_LOGGER_NAME);private static final long MS_24_HOUR = 24 * 3600 * 1000;// 核心数据结构:topic -> consumer group -> ConsumerFilterDataprivate ConcurrentMap<String/*Topic*/, FilterDataMapByTopic>filterDataByTopic = new ConcurrentHashMap<String/*Topic*/, FilterDataMapByTopic>(256);private transient BrokerController brokerController;// 布隆过滤器private transient BloomFilter bloomFilter;public ConsumerFilterManager() {// just for testthis.bloomFilter = BloomFilter.createByFn(20, 64);}public ConsumerFilterManager(BrokerController brokerController) {this.brokerController = brokerController;this.bloomFilter = BloomFilter.createByFn(brokerController.getBrokerConfig().getMaxErrorRateOfBloomFilter(),brokerController.getBrokerConfig().getExpectConsumerNumUseFilter());// then set bit map length of store config.brokerController.getMessageStoreConfig().setBitMapLengthConsumeQueueExt(this.bloomFilter.getM());}/*** Build consumer filter data.Be care, bloom filter data is not included.** @return maybe null*/public static ConsumerFilterData build(final String topic, final String consumerGroup,final String expression, final String type,final long clientVersion) {if (ExpressionType.isTagType(type)) {return null;}ConsumerFilterData consumerFilterData = new ConsumerFilterData();consumerFilterData.setTopic(topic);consumerFilterData.setConsumerGroup(consumerGroup);consumerFilterData.setBornTime(System.currentTimeMillis());consumerFilterData.setDeadTime(0);consumerFilterData.setExpression(expression);consumerFilterData.setExpressionType(type);consumerFilterData.setClientVersion(clientVersion);try {consumerFilterData.setCompiledExpression(FilterFactory.INSTANCE.get(type).compile(expression));} catch (Throwable e) {log.error("parse error: expr={}, topic={}, group={}, error={}", expression, topic, consumerGroup, e.getMessage());return null;}return consumerFilterData;}/*** 在指定消费组注册消费者过滤数据* @param consumerGroup* @param subList*/public void register(final String consumerGroup, final Collection<SubscriptionData> subList) {for (SubscriptionData subscriptionData : subList) {register(subscriptionData.getTopic(),consumerGroup,subscriptionData.getSubString(),subscriptionData.getExpressionType(),subscriptionData.getSubVersion());}// make illegal topic dead.Collection<ConsumerFilterData> groupFilterData = getByGroup(consumerGroup);Iterator<ConsumerFilterData> iterator = groupFilterData.iterator();while (iterator.hasNext()) {ConsumerFilterData filterData = iterator.next();boolean exist = false;for (SubscriptionData subscriptionData : subList) {if (subscriptionData.getTopic().equals(filterData.getTopic())) {exist = true;break;}}if (!exist && !filterData.isDead()) {filterData.setDeadTime(System.currentTimeMillis());log.info("Consumer filter changed: {}, make illegal topic dead:{}", consumerGroup, filterData);}}}public boolean register(final String topic, final String consumerGroup, final String expression,final String type, final long clientVersion) {// 不支持tag类型if (ExpressionType.isTagType(type)) {return false;}if (expression == null || expression.length() == 0) {return false;}// 获取topic对应的消费者过滤数据FilterDataMapByTopic filterDataMapByTopic = this.filterDataByTopic.get(topic);if (filterDataMapByTopic == null) {FilterDataMapByTopic temp = new FilterDataMapByTopic(topic);FilterDataMapByTopic prev = this.filterDataByTopic.putIfAbsent(topic, temp);filterDataMapByTopic = prev != null ? prev : temp;}// 创建布隆过滤器数据BloomFilterData bloomFilterData = bloomFilter.generate(consumerGroup + "#" + topic);// 注册过滤数据到topicreturn filterDataMapByTopic.register(consumerGroup, expression, type, bloomFilterData, clientVersion);}// 取消注册消费者过滤数据public void unRegister(final String consumerGroup) {for (Entry<String, FilterDataMapByTopic> entry : filterDataByTopic.entrySet()) {entry.getValue().unRegister(consumerGroup);}}public ConsumerFilterData get(final String topic, final String consumerGroup) {if (!this.filterDataByTopic.containsKey(topic)) {return null;}if (this.filterDataByTopic.get(topic).getGroupFilterData().isEmpty()) {return null;}return this.filterDataByTopic.get(topic).getGroupFilterData().get(consumerGroup);}// 获取消费组下所有过滤数据public Collection<ConsumerFilterData> getByGroup(final String consumerGroup) {Collection<ConsumerFilterData> ret = new HashSet<ConsumerFilterData>();Iterator<FilterDataMapByTopic> topicIterator = this.filterDataByTopic.values().iterator();while (topicIterator.hasNext()) {FilterDataMapByTopic filterDataMapByTopic = topicIterator.next();Iterator<ConsumerFilterData> filterDataIterator = filterDataMapByTopic.getGroupFilterData().values().iterator();while (filterDataIterator.hasNext()) {ConsumerFilterData filterData = filterDataIterator.next();if (filterData.getConsumerGroup().equals(consumerGroup)) {ret.add(filterData);}}}return ret;}// 获取topic下所有过滤数据public final Collection<ConsumerFilterData> get(final String topic) {if (!this.filterDataByTopic.containsKey(topic)) {return null;}if (this.filterDataByTopic.get(topic).getGroupFilterData().isEmpty()) {return null;}return this.filterDataByTopic.get(topic).getGroupFilterData().values();}public BloomFilter getBloomFilter() {return bloomFilter;}@Overridepublic String encode() {return encode(false);}@Overridepublic String configFilePath() {if (this.brokerController != null) {// 配置存储路径 config/consumerFilter.jsonreturn BrokerPathConfigHelper.getConsumerFilterPath(this.brokerController.getMessageStoreConfig().getStorePathRootDir());}return BrokerPathConfigHelper.getConsumerFilterPath("./unit_test");}// 将json字符串反序列化为ConsumerFilterManager对象@Overridepublic void decode(final String jsonString) {ConsumerFilterManager load = RemotingSerializable.fromJson(jsonString, ConsumerFilterManager.class);if (load != null && load.filterDataByTopic != null) {boolean bloomChanged = false;for (Entry<String, FilterDataMapByTopic> entry : load.filterDataByTopic.entrySet()) {FilterDataMapByTopic dataMapByTopic = entry.getValue();if (dataMapByTopic == null) {continue;}for (Entry<String, ConsumerFilterData> groupEntry : dataMapByTopic.getGroupFilterData().entrySet()) {ConsumerFilterData filterData = groupEntry.getValue();if (filterData == null) {continue;}try {filterData.setCompiledExpression(FilterFactory.INSTANCE.get(filterData.getExpressionType()).compile(filterData.getExpression()));} catch (Exception e) {log.error("load filter data error, " + filterData, e);}// check whether bloom filter is changed// if changed, ignore the bit map calculated before.if (!this.bloomFilter.isValid(filterData.getBloomFilterData())) {bloomChanged = true;log.info("Bloom filter is changed!So ignore all filter data persisted! {}, {}", this.bloomFilter, filterData.getBloomFilterData());break;}log.info("load exist consumer filter data: {}", filterData);if (filterData.getDeadTime() == 0) {// we think all consumers are dead when loadlong deadTime = System.currentTimeMillis() - 30 * 1000;filterData.setDeadTime(deadTime <= filterData.getBornTime() ? filterData.getBornTime() : deadTime);}}}if (!bloomChanged) {this.filterDataByTopic = load.filterDataByTopic;}}}// 将ConsumerFilterManager对象序列化为json字符串@Overridepublic String encode(final boolean prettyFormat) {// clean{clean();}return RemotingSerializable.toJson(this, prettyFormat);}// 清理过期的过滤数据public void clean() {Iterator<Map.Entry<String, FilterDataMapByTopic>> topicIterator = this.filterDataByTopic.entrySet().iterator();while (topicIterator.hasNext()) {Map.Entry<String, FilterDataMapByTopic> filterDataMapByTopic = topicIterator.next();Iterator<Map.Entry<String, ConsumerFilterData>> filterDataIterator= filterDataMapByTopic.getValue().getGroupFilterData().entrySet().iterator();while (filterDataIterator.hasNext()) {Map.Entry<String, ConsumerFilterData> filterDataByGroup = filterDataIterator.next();ConsumerFilterData filterData = filterDataByGroup.getValue();if (filterData.howLongAfterDeath() >= (this.brokerController == null ? MS_24_HOUR : this.brokerController.getBrokerConfig().getFilterDataCleanTimeSpan())) {log.info("Remove filter consumer {}, died too long!", filterDataByGroup.getValue());filterDataIterator.remove();}}if (filterDataMapByTopic.getValue().getGroupFilterData().isEmpty()) {log.info("Topic has no consumer, remove it! {}", filterDataMapByTopic.getKey());topicIterator.remove();}}}public ConcurrentMap<String, FilterDataMapByTopic> getFilterDataByTopic() {return filterDataByTopic;}public void setFilterDataByTopic(final ConcurrentHashMap<String, FilterDataMapByTopic> filterDataByTopic) {this.filterDataByTopic = filterDataByTopic;}public static class FilterDataMapByTopic {// 核心数据结构:consumer group -> ConsumerFilterDataprivate ConcurrentMap<String/*consumer group*/, ConsumerFilterData>groupFilterData = new ConcurrentHashMap<String, ConsumerFilterData>();private String topic;public FilterDataMapByTopic() {}public FilterDataMapByTopic(String topic) {this.topic = topic;}// 取消注册某个消费组的过滤器public void unRegister(String consumerGroup) {if (!this.groupFilterData.containsKey(consumerGroup)) {return;}ConsumerFilterData data = this.groupFilterData.get(consumerGroup);if (data == null || data.isDead()) {return;}long now = System.currentTimeMillis();log.info("Unregister consumer filter: {}, deadTime: {}", data, now);data.setDeadTime(now);}public boolean register(String consumerGroup, String expression, String type, BloomFilterData bloomFilterData,long clientVersion) {ConsumerFilterData old = this.groupFilterData.get(consumerGroup);if (old == null) {// 构建过滤器数据ConsumerFilterData consumerFilterData = build(topic, consumerGroup, expression, type, clientVersion);if (consumerFilterData == null) {return false;}// 设置布隆过滤器consumerFilterData.setBloomFilterData(bloomFilterData);// 放入内存数据结构old = this.groupFilterData.putIfAbsent(consumerGroup, consumerFilterData);if (old == null) {log.info("New consumer filter registered: {}", consumerFilterData);return true;} else {if (clientVersion <= old.getClientVersion()) {if (!type.equals(old.getExpressionType()) || !expression.equals(old.getExpression())) {log.warn("Ignore consumer({} : {}) filter(concurrent), because of version {} <= {}, but maybe info changed!old={}:{}, ignored={}:{}",consumerGroup, topic,clientVersion, old.getClientVersion(),old.getExpressionType(), old.getExpression(),type, expression);}if (clientVersion == old.getClientVersion() && old.isDead()) {reAlive(old);return true;}return false;} else {this.groupFilterData.put(consumerGroup, consumerFilterData);log.info("New consumer filter registered(concurrent): {}, old: {}", consumerFilterData, old);return true;}}} else {// 当前版本号小于旧的版本号if (clientVersion <= old.getClientVersion()) {if (!type.equals(old.getExpressionType()) || !expression.equals(old.getExpression())) {log.info("Ignore consumer({}:{}) filter, because of version {} <= {}, but maybe info changed!old={}:{}, ignored={}:{}",consumerGroup, topic,clientVersion, old.getClientVersion(),old.getExpressionType(), old.getExpression(),type, expression);}if (clientVersion == old.getClientVersion() && old.isDead()) {reAlive(old);return true;}return false;}// 新版本号大于旧的版本号boolean change = !old.getExpression().equals(expression) || !old.getExpressionType().equals(type);if (old.getBloomFilterData() == null && bloomFilterData != null) {change = true;}if (old.getBloomFilterData() != null && !old.getBloomFilterData().equals(bloomFilterData)) {change = true;}// if subscribe data is changed, or consumer is died too long.if (change) {// 构建过滤器数据ConsumerFilterData consumerFilterData = build(topic, consumerGroup, expression, type, clientVersion);if (consumerFilterData == null) {// new expression compile error, remove old, let client report error.this.groupFilterData.remove(consumerGroup);return false;}consumerFilterData.setBloomFilterData(bloomFilterData);// 设置过滤器数据this.groupFilterData.put(consumerGroup, consumerFilterData);log.info("Consumer filter info change, old: {}, new: {}, change: {}",old, consumerFilterData, change);return true;} else {// 版本号一致,更新过滤器数据old.setClientVersion(clientVersion);if (old.isDead()) {reAlive(old);}return true;}}}protected void reAlive(ConsumerFilterData filterData) {long oldDeadTime = filterData.getDeadTime();filterData.setDeadTime(0);log.info("Re alive consumer filter: {}, oldDeadTime: {}", filterData, oldDeadTime);}public final ConsumerFilterData get(String consumerGroup) {return this.groupFilterData.get(consumerGroup);}public final ConcurrentMap<String, ConsumerFilterData> getGroupFilterData() {return this.groupFilterData;}public void setGroupFilterData(final ConcurrentHashMap<String, ConsumerFilterData> groupFilterData) {this.groupFilterData = groupFilterData;}public String getTopic() {return topic;}public void setTopic(final String topic) {this.topic = topic;}}
}

相关文章:

RocketMQ源码 Broker-ConsumerFilterManager 消费者数据过滤管理组件源码分析

前言 ConsumerFilterManager 继承了ConfigManager配置管理组件&#xff0c;拥有将内存数据持久化到磁盘文件consumerFilter.json的能力。它主要负责&#xff0c;对在消费者拉取消息时&#xff0c;进行消息数据过滤&#xff0c;且只针对使用表达式过滤的消费者有效。 源码版本&…...

数据挖掘-07-航空公司客户价值分析(包括数据和代码)

文章目录 0. 数据代码下载1. 背景与挖掘目标2. 导入相关库&#xff0c;加载数据2.1客户基本信息分布a. 绘制会员性别比例饼图b. 绘制会员各级别人数条形图c. 绘制年龄分布图 2.2 客户乘机信息分布分析a. 绘制客户飞行次数箱线图b. 绘制客户总飞行公里数箱线图 2.3 客户积分信息…...

浏览器 css 默认的字体图表

以下是一些常见的浏览器&#xff08;PC端&#xff09;中网站 CSS 默认字体及其对应的字体系列&#xff08;font family&#xff09;&#xff1a; 浏览器默认字体字体系列&#xff08;font family&#xff09;ChromeArial, sans-serif“Arial”, “Helvetica Neue”, Helvetica…...

JAVA:注册表窗口的实现

目录 题目要求&#xff1a; 思路大意&#xff1a; 窗体的实现&#xff1a; 窗口A&#xff1a; 窗口B&#xff1a; 窗体之间的构思&#xff1a; 关键代码的实现&#xff1a; 窗口A&#xff1a; 封装列表&#xff1a; 窗口B&#xff1a; 题目要求&#xff1a; 使用…...

Liunx Centos 防火墙操作

liunx centos 防火墙 查看防火墙状态 systemctl status firewalld查看已经开放的端口 firewall-cmd --list-ports添加端口3306 firewall-cmd --zonepublic --add-port3306/tcp --permanent重启防火墙 firewall-cmd --reload数据库开放账号可以外网登陆 mysql -u root -p …...

VirtualBox 和 Vagrant 快速安装 Centos7 报错

VirtualBox 和 Vagrant 快速安装 Centos7 报错 今天尝试用 VirtualBox 和 Vagrant 快速安装 Centos7&#xff0c;BUG 多多&#xff01; 1&#xff09;下载 6.1.26 版本 VirtualBox&#xff0c;Windows11 不兼容&#xff1f;&#xff1f;&#xff1f;什么鬼&#xff1f; 解决…...

使用Python进行数学四则运算

当我们讨论到Python中的计算问题时&#xff0c;我们必然涉及到加法运算符&#xff08;&#xff09;、减法运算符&#xff08;-&#xff09;、乘法运算符&#xff08;*&#xff09;以及除法运算符&#xff08;/&#xff09;这四大常见的算术运算。下面&#xff0c;我将为您展示如…...

成都工业学院2021级操作系统专周课程设计FCFS,SSTF,SCAN,LOOK算法的实现

运行环境 操作系统&#xff1a;Windows 11 家庭版 运行软件&#xff1a;CLion 2023.2.2 源代码文件 #include <iostream> #include <vector> #include <algorithm> #include <random> using namespace std;// 生成随机数 int generateRandomNumber…...

【51单片机系列】矩阵按键扩展实验

本文对矩阵按键的一个扩展&#xff0c;利用矩阵按键和动态数码管设计一个简易计算器。代码参考&#xff1a;https://blog.csdn.net/weixin_47060099/article/details/106664393 实现功能&#xff1a;使用矩阵按键&#xff0c;实现一个简易计算器&#xff0c;将计算数据及计算结…...

大数据云计算——Docker环境下部署Hadoop集群及运行集群案列

大数据云计算——Docker环境下部署Hadoop集群及运行集群案列 本文着重介绍了在Docker环境下部署Hadoop集群以及实际案例中的集群运行。首先&#xff0c;文章详细解释了Hadoop的基本概念和其在大数据处理中的重要性&#xff0c;以及为何选择在Docker环境下部署Hadoop集群。接着&…...

计算机网络链路层(期末、考研)

计算机网络总复习链接&#x1f517; 目录 组帧差错控制检错编码纠错编码 流量控制与可靠传输机制流量控制、可靠传输与滑动窗口机制单帧窗口与停止-等待协议多帧滑动窗口与后退N帧协议&#xff08;GBN&#xff09;多帧滑动窗口与选择重传协议 介质访问控制信道划分介质访问控制…...

洛谷 P8794 [蓝桥杯 2022 国 A] 环境治理

文章目录 [蓝桥杯 2022 国 A] 环境治理题目链接题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 思路解析CODE给点思考 [蓝桥杯 2022 国 A] 环境治理 题目链接 https://www.luogu.com.cn/problem/P8794 题目描述 LQ 国拥有 n n n 个城市&#xff0c;从 0 0 …...

力扣面试150题 | 买卖股票的最佳时期

力扣面试150题 &#xff5c; 买卖股票的最佳时期 题目描述解题思路代码实现 题目描述 121.买卖股票的最佳时期 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一…...

uniapp 之 图片 视频 文件上传

<view class"" style"padding: 24rpx 0"><text>相关资料 <text class"fs-26 color-666">&#xff08;图片、视频、文档不超过9个&#xff09;</text> </text><view class"flex align-center" style&…...

MIT线性代数笔记-第28讲-正定矩阵,最小值

目录 28.正定矩阵&#xff0c;最小值打赏 28.正定矩阵&#xff0c;最小值 由第 26 26 26讲的末尾可知在矩阵为实对称矩阵时&#xff0c;正定矩阵有以下四种判定方法&#xff08;都是充要条件&#xff09;&#xff1a; 所有特征值都为正左上角所有 k k k阶子矩阵行列式都为正&…...

Python:五种算法RFO、GWO、DBO、HHO、SSA求解23个测试函数

一、五种算法介绍 &#xff08;1&#xff09;红狐优化算法&#xff08;Red fox optimization&#xff0c;RFO&#xff09; &#xff08;2&#xff09;灰狼优化算法(Grey Wolf Optimizer&#xff0c;GWO) &#xff08;3&#xff09;蜣螂优化算法&#xff08;Dung beetle opti…...

如何参与开源项目

大家好&#xff0c;受卡哥邀请&#xff0c;和大家分享一下开源活动的相关经验。首先简要自我介绍一下&#xff0c;我目前在一所985研二在读&#xff0c;主要学习大数据方向&#xff0c;从去年开始参与开源活动近一年时间&#xff0c;也对多个Apache框架有所贡献。 由于学校或专…...

twitter开发如何避坑

此篇介绍在twitter开发过程中遇到的坑&#xff08;尤其是费用的坑&#xff09;。 一坑&#xff1a;免费接口少&#xff01; 刚开始申请免费API使用的时候&#xff0c;twitter官方只会给你三个免费接口使用。 发twitter、删推文、查看用户信息。 这三个接口远远不够开发中使用…...

人工智能算法合集

人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;作为当今世界最热门的技术领域之一&#xff0c;正日益改变着我们的生活方式、工作方式甚至整个社会结构。在人工智能领域中&#xff0c;算法是至关重要的一环&#xff0c;它们是实现人工智能技术应用的核…...

PythonStudio:一款国人写的python及窗口开发编辑IDE,可以替代pyqt designer等设计器了

本款软件只有十几兆&#xff0c;功能算是强大的&#xff0c;国人写的&#xff0c;很不错的python界面IDE.顶部有下载链接。下面有网盘下载链接&#xff0c;或者从官网直接下载。 目前产品免费&#xff0c;以后估计会有收费版本。主页链接&#xff1a;PythonStudio-硅量实验室 作…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...