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

Spring Data Redis + RabbitMQ - 基于 string 实现缓存、计数功能(同步数据)

目录

一、Spring Data Redis

1.1、缓存功能

1.1.1、分析

1.1.2、案例实现

1.1.3、效果演示

1.2、计数功能(Redis + RabbitMQ)

1.2.1、分析

1.2.2、案例实现


一、Spring Data Redis


1.1、缓存功能

1.1.1、分析

使用 redis 作为缓存, MySQL 作为数据库组成的架构

整体思路:

应用服务器访问数据的时候,先查询 Redis,如果 Redis 上存在该数据,就从 Redis 中取数据直接交给应用服务器,不用继续访问数据库了;如果 Redis 上不存在该数据,就会去 MySQL 中把读到的结构返回给应用服务器,同时,把这个数据也写入到 Redis 中.

由于 Redis 这样的缓存经常用来存储 “热点数据”,也就是高频使用的数据,那什么样的数据算高频呢?这里暗含了一层假设,某个数据一旦被用到了,那么可能在最近这段时间就可能被反复用到.

随着时间推移,越来越多的 key 在 redis 上访问不到,那 redis 的数据不是越来越多么?

  1. 把数据写给 redis 的同时,会给这个 key 设置一个过期时间.
  2. Redis 也有内存不足的时候,因此提供了 淘汰策略(之前的文章展开讲过).

1.1.2、案例实现

例如论坛网站,有些帖子的访问评论很高,就需要设置成热点文章,缓存起来(比起去 MySQL 数据库中查询文章要快的多). 

实现思路:

        根据上面理论,暗含假设当前使用的文章就是热点文章,也就是说,如果在缓存中有该文章,就直接返回,如果没有,就去数据库中查,然后再缓存起来,同时设置 30min(不同场景合理分配) 的过期时间.

帖子实体类.

@Data
public class Article {private String title;private String content;}

文章 mapper.

@Mapper
public interface ArticleMapper {/*** 根据 id 查询文章* @param id* @return*/Article selectArticleById(@Param("id") Integer id);}
    <select id="selectArticleById" resultType="com.example.cyk.cache.Article">select * from article where id = #{id};</select>

帖子 controller

@RestController
@RequestMapping("/article")
public class ArticleController {@Autowiredprivate IArticleService articleService;@GetMapping("/get")public HashMap<String, Object> get(@NonNull Integer id) {//1.获取文章服务Article article = articleService.getArticleInfo(id);//2.返回响应return HandlerResponse(1000, "操作成功", article);}/*** 处理返回格式* @param code* @param msg* @param data* @return*/private HashMap<String, Object> HandlerResponse(Integer code, String msg, Object data) {HashMap<String, Object> result = new HashMap<>();result.put("code", code);result.put("msg", msg);result.put("data", data);return result;}}

帖子 service .

@Slf4j
@Service
public class ArticleService implements IArticleService {@Autowiredprivate ArticleMapper articleMapper;@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic Article getArticleInfo(Integer id) {//1.非空校验if(id == null) {log.warn("文章 id 为空");throw new RuntimeException("文章 id 为空");}//2.先去 redis 上看有没有文章对应的这个id//我这里约定 redis 上存储格式://key: art:id//value: $title$content  ($ 是分隔符)//例如 key: art:1    value: $决定$今天要好好学习String articleInfo = redisTemplate.opsForValue().get("art:" + id);if(articleInfo != null) {//存在直接返回log.info("从 redis 中获取到文章数据");//1) 解析格式Article article = analysisArticle(articleInfo);//2) 返回数据return article;}//3.redis 上没有数据,因此需要从 mysql 中取Article article = articleMapper.selectArticleById(id);if(article == null) {log.warn("文章不存在");throw new RuntimeException("文章不存在!");}//4.将文章存到 redis 中//1) 合成 redis 所需格式的文章articleInfo = synthesisArticle(article);//2) 设置 5 分钟过期时间(为了演示效果)redisTemplate.opsForValue().set("art:" + id, articleInfo, 5, TimeUnit.SECONDS);log.info("从 mysql 中获取到文章数据");return article;}/*** 合成 redis 需要的格式(提前约定好的)* @param article* @return*/private String synthesisArticle(Article article) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("$");stringBuilder.append(article.getTitle());stringBuilder.append("$");stringBuilder.append(article.getContent());return stringBuilder.toString();}/*** 解析文章格式* @param articleInfo* @return*/private Article analysisArticle(String articleInfo) {Article article = new Article();String title = articleInfo.split("\\$")[1];String content = articleInfo.split("\\$")[2];article.setTitle(title);article.setContent(content);return article;}}

1.1.3、效果演示

1.2、计数功能(Redis + RabbitMQ)

1.2.1、分析

许多都会使应用用 Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,例如网站视频的播放量,点赞数量......

Ps:这些都是相比较 MySQL 数据库而言的,Redis 可以通过简单的键值对操作完成计数任务并且实在内存中完成的,而 MySQL 就需要先查询数据库,然后 +1,然后再存入数据库,是在需要进行硬盘存储的

1.2.2、案例实现

实现思路:

        假设,用户点击某个帖子,此时需要进行访问量 + 1 的操作,这时候应用服务器就会直接去操作 Redis ,执行 incr 命令,然后将返回的数据反馈给用户,最后 Redis 会以异步的方式(RabbitMQ 实现异步)将播放量同步到 MySQL 数据库中(异步就表示:这里并不是每一个播放请求,都需要立即写入数据~ 至于什么时候写入,需要根据实际的业务需求场景而定),将数据持久化.

Ps:实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按 照不同维度计数、避免单点问题、数据持久化到底层数据源等。

文章实体类

@Data
public class Article implements Serializable {private Integer id;private String title;private String content;private Long visits; //访问量}

rabbit 交换机、队列、绑定配置.

public class MqFinal {//处理文章的直接交换机public static final String UPDATE_DIRECT = "article.update.direct";//用于修改文章数据的队列public static final String UPDATE_QUEUE = "article.update.queue";//bindingKeypublic static final String UPDATE_KEY = "article.update.key";}
@Configuration
public class MqConfig {/*** 消息转化器* @return*/@Beanpublic MessageConverter jsonMessageConverter() {return new Jackson2JsonMessageConverter();}@Beanpublic DirectExchange ArticleDirectExchange() {return new DirectExchange(MqFinal.UPDATE_DIRECT, true, false);}@Beanpublic Queue ArticleUpdateQueue() {return new Queue(MqFinal.UPDATE_QUEUE, true);}@Beanpublic Binding ArticleUpdateBinding() {return BindingBuilder.bind(ArticleUpdateQueue()).to(ArticleDirectExchange()).with(MqFinal.UPDATE_KEY);}}

mq 监听配置

@Slf4j
@Component
public class MqListenerArticle {@Autowiredprivate ArticleMapper articleMapper;/*** 同步数据库*/@RabbitListener(queues = MqFinal.UPDATE_QUEUE)public void syncVisits(HashMap<String, Object> data) {Integer id = (Integer) data.get("id");// Rabbitmq 这里有一个问题,Map<String, Object> 中 Object 传入为 Long 类型,需要用 Integer 来接受,否则报错// 因此发送消息之前,体现将 Long 类型转化为 String,接收到消息之后只需要将 String 转化为 Long 即可String visits = (String) data.get("visits");articleMapper.updateArticleVisits(id, Long.valueOf(visits));log.info("访问量数据同步完成!");}}

访问量增加服务(这里为了可读性,只展示了本业务的核心逻辑)

    @Overridepublic Article getArticleInfo(Integer id) {//1.非空校验if(id == null) {log.warn("文章 id 为空");throw new RuntimeException("文章 id 为空");}//2.访问量 +1//注意:incr 这个命令执行时,即使 key 不存在,也会自动生成 key,然后自增Long visits = redisTemplate.opsForValue().increment("v_art:" + id);//3.rabbitmq 实现异步数据同步(发送一个消息即可)HashMap<String, Object> visitsInfo = new HashMap<>();visitsInfo.put("id", id);visitsInfo.put("visits", visits.toString()); //转化原因前面解释过了rabbitTemplate.convertAndSend(MqFinal.UPDATE_DIRECT, MqFinal.UPDATE_KEY, visitsInfo);//4.获取文章数据//业务逻辑(这里为了可读性,就先不展示这里了)......//5.放入文章Article article = new Article();article.setVisits(visits);article.setId(id);return article;}

相关文章:

Spring Data Redis + RabbitMQ - 基于 string 实现缓存、计数功能(同步数据)

目录 一、Spring Data Redis 1.1、缓存功能 1.1.1、分析 1.1.2、案例实现 1.1.3、效果演示 1.2、计数功能&#xff08;Redis RabbitMQ&#xff09; 1.2.1、分析 1.2.2、案例实现 一、Spring Data Redis 1.1、缓存功能 1.1.1、分析 使用 redis 作为缓存&#xff0c; M…...

Facebook Developer 的 HashCode

在 Android 中&#xff0c;您可以使用 Facebook SDK 提供的工具来生成您的应用程序的哈希码&#xff08;hash code&#xff09;&#xff0c;以便在 Facebook 开发者帐户中配置您的应用程序。 要生成哈希码&#xff0c;您可以使用以下步骤&#xff1a; 打开终端或命令提示符&am…...

下载使用 ant design Pro 中遇到的一些问题

文章目录 npm 版本问题在idea终端输入命令报错&#xff1a;error:0308010C:digital envelope routines::unsupported npm 版本问题 npm v9.6.3 is known not to run on Node.js v19.9.0. This version of npm supports the following node versions: ^14.17.0 || ^16.13.0 || …...

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…...

[动态规划] (七) 路径问题:LCR 166.剑指offer 47. 珠宝的最高价值

[动态规划] (七) 路径问题&#xff1a;LCR 166./剑指offer 47. 珠宝的最高价值 文章目录 [动态规划] (七) 路径问题&#xff1a;LCR 166./剑指offer 47. 珠宝的最高价值题目解析解题思路状态表示状态转移方程初始化和填表顺序 返回值代码实现总结 LCR 166. 珠宝的最高价值 题目…...

Mysql进阶-SQL优化篇

插入数据 insert 我们需要一次性往数据库表中插入多条记录&#xff0c;可以从以下三个方面进行优化。 批量插入数据 一条insert语句插入多个数据&#xff0c;但要注意&#xff0c;每个insert语句最好插入500-1000行数据&#xff0c;就得重新写另一条insert语句 Insert into…...

VueI18n中英文切换 vue2.0

1: npm install --save vue-i18n8.0.0 &#xff08;版本不要高了&#xff0c;不然报错&#xff09; 2&#xff1a;创建相关文件 3&#xff1a;main.js文件配置 //i18n插件 import VueI18n from vue-i18n // element-ui多语言文件 import locale from element-ui/lib/locale;…...

VUE组件间通信的七种方式

目录 1、 props / $emit &#xff08;1&#xff09;父组件向子组件传值&#xff08;props的用法&#xff09; &#xff08;2&#xff09;子组件向父组件传递数据&#xff08;$emit的用法&#xff09; 2、ref / $refs 用法&#xff1a; 3、eventBus事件总线&#xff08;$e…...

问chatgpt最近生活的困难

你知道吗&#xff0c;因为我做的所有的事情没有任何目的性&#xff0c;所以曾经过的很好&#xff0c;这种很好是一种逃避式的好&#xff0c;怎么说呢&#xff1f;遇到困难了&#xff0c;那就不做了&#xff0c;换下一个项目。比如打游戏&#xff0c;如果我这局玩王者荣耀&#…...

Flink源码解析八之任务调度和负载均衡

源码概览 jobmanager scheduler:这部分与 Flink 的任务调度有关。 CoLocationConstraint:这是一个约束类,用于确保某些算子的不同子任务在同一个 TaskManager 上运行。这通常用于状态共享或算子链的情况。CoLocationGroup & CoLocationGroupImpl:这些与 CoLocationCon…...

4.3 传送门

算法设计与分析 4.3 传送门 题目描述 现在有 n 个传送门&#xff0c;你处在第一个传送门的位置&#xff0c;第 i 个传送门可以将你传送到第 i-a[i] 到第 ia[i] 范围内的任意一个传送门&#xff0c;请问你最少需要几次操作&#xff0c;使得你可以传送到最后一个传送门的位置。 …...

NLP之Bert介绍和简单示例

文章目录 1. Bert 介绍2. 代码示例2.1 代码流程 1. Bert 介绍 2. 代码示例 from transformers import AutoTokenizertokenizer AutoTokenizer.from_pretrained("bert-base-chinese") input_ids tokenizer.encode(欢迎来到Bert世界, return_tensorstf) print(input…...

【Windows】Google和火狐浏览器禁用更新的操作方式

想必很多网民常用的浏览器是Edge&#xff0c;Google&#xff0c;火狐这三种&#xff0c;但是浏览器都有后台自动更新&#xff0c;更新提示会一直显示&#xff0c;要用户去点击才关掉&#xff0c;有点强迫症的用户就会想要把它一直关掉&#xff0c;可每次打开都关不掉&#xff0…...

关于编程不得不说的事

这些年&#xff0c;互联网爆炸式的发展&#xff0c;促生了无数程序员&#xff0c;也促生了大量 IT培训机构。短短数年间&#xff0c;科班出生的程序员和培训机构出生的程序员呈指数增长。程序员的职业也不再是金饭碗。写了这么多代码&#xff0c;有些感触&#xff0c;所以写下来…...

2.4G合封芯片 XL2422,集成M0核MCU,高性能 低功耗

XL2422芯片是一款高性能低功耗的SOC集成无线收发芯片&#xff0c;集成M0核MCU&#xff0c;工作在2.400~2.483GHz世界通用ISM频段。该芯片集成了射频接收器、射频发射器、频率综合器、GFSK调制器、GFSK解调器等功能模块&#xff0c;并且支持一对多线网和带ACK的通信模式。发射输…...

【QT基础入门 控件篇】QLineEdit 基础、高级和样式表使用详解

一、QLineEdit简介 QLineEdit是一个单行文本编辑器&#xff0c;它可以让用户输入和编辑纯文本&#xff0c;也可以设置一些有用的编辑功能&#xff0c;如撤销和重做、剪切和粘贴、拖放等。QLineEdit: 可以根据不同的回显模式&#xff08;echoMode&#xff09;来显示不同的输入内…...

网络安全(网络安全)小白自学

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全…...

dupeGuru 清理微信重复文件

本文摘录于&#xff1a;https://www.bilibili.com/video/BV13p4y1G75Y/?spm_id_from333.337.search-card.all.click&vd_source483e5c52353ea59d1a5eadac7737591a只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 微信用了七八年,文件…...

华为RS设备状态及接口配置命令

1、查看硬件信息 ①查看序列号 查看整机序列号 display esn display sn ②、查看功率 电源功率 display power 查看光模块功率 display transceiver interface gigabitethernet 1/0/0 verbose ③、查看风扇 display fan ④、查看温度 display temperature all ⑤、查看硬…...

单链表的应用(2)

环形链表的约瑟夫问题 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数&#xff0c;报到 m 的人离开。 下一个人继续从 1 开始报数。 n-1 轮结束以后&#xff0c;只剩下一个人&#xff0c;问最后留下的这个人编号是多少&#xff1f; 利用链表实现 思路&#xff1…...

【Boost | C++】使用Boost库创建文件夹

#include <boost/filesystem.hpp> #include <iostream> bool CreateDirectory(const std::string &dir_path) {try {if (...

月报总结|Moonbeam 10月份大事一览

万圣节快乐&#xff01;时间一晃眼&#xff0c;10月已经迈入尾声&#xff0c;也即将迎来寒冷的冬天。但与季节相反&#xff0c;加密产业近期的发展可以说是高潮起伏&#xff0c;热度不断攀升。Moonbeam在10月中也发布了许多重大的更新&#xff0c;如Uniswap V3前段上线、众贷DO…...

Latex安装记录

Title:Latex 基本概念 Tex:是一种具有编译和排版功能的基础语言&#xff0c;相当于C语言。 Latex:&#xff1a;LaTex是 Tex 的扩展版本&#xff0c;拥有多种宏包&#xff0c;能实现比 Tex 更多的功能。 TexLive&#xff1a;是一种 Tex 语言的发行版本。 Texstudio: 一种软件相…...

JavaEE-博客系统2(功能设计)

本部分内容&#xff1a;实现博客列表页&#xff1b;web程序问题的分析方法&#xff1b;实现博客详情页&#xff1b; 该部分的代码如下&#xff1a; WebServlet("/blog") public class BlogServlet extends HttpServlet {//Jackson ObjectMapper类(com.fasterxml.jac…...

2023年【高处安装、维护、拆除】免费试题及高处安装、维护、拆除找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 高处安装、维护、拆除免费试题根据新高处安装、维护、拆除考试大纲要求&#xff0c;安全生产模拟考试一点通将高处安装、维护、拆除模拟考试试题进行汇编&#xff0c;组成一套高处安装、维护、拆除全真模拟考试试题&a…...

antv/g6之交互模式mode

什么是mode 在 AntV G6 中&#xff0c;“mode” 是用于配置图表交互模式的一种属性。通过设置 “mode”&#xff0c;可以控制图表的行为&#xff0c;以满足不同的交互需求。可能在不同的场景需要展现的交互行为不一样。比如查看模式下点击一个点就选中的状态&#xff0c;在编辑…...

基于8086电压表系统仿真系统设计

**单片机设计介绍&#xff0c;1665基于8051单片机与1601LCD的计算器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 一个基于8086的电压表系统仿真系统可以分为硬件和软件两部分。 硬件部分包括输入设备&#xff08;例如模拟…...

Docker与微服务实战——基础篇

Docker与微服务实战——基础篇 第一章 Docker 简介1.1 docker 理念1.2 容器与虚拟机比较 第二章 Docker 安装2.1 前提说明2.2 Docker的基本组成2.2.1 镜像&#xff08;image&#xff09;2.2.2 容器&#xff08;container&#xff09;2.2.3 仓库&#xff08;repository&#xff…...

旧手机搭建linuxcentos

centos服务器搭建termux搭建centos旧手机搭建linux服务器ubuntu旧手机搭建网站旧手机搭建linux debian ubuntu centos 旧手机搭建宝塔搭建 32位Linux搭建宝塔 Linuxdeploy搭建宝塔 旧手机搭建服务器有需要的来 包答疑包售后 Linuxdeploy需要root mobile搭建服务器 脚本/工具...

使用pandas处理excel文件【Demo】

一、代码示例 import pandas as pd from pandas import Series,DataFrame from pandasql import sqldf import matplotlib.pyplotidInfos DataFrame(pd.read_excel(home_data.xlsx))print(idInfos.head(2))print(idInfos.dtypes)# print(idInfos[:][姓名]) # 自定义一个函数s…...