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;}

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

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

相关文章

甘特图组件DHTMLX Gantt用例 - 如何拆分任务和里程碑项目路线图

创建一致且引人注意的视觉样式是任何项目管理应用程序的重要要求&#xff0c;这就是为什么我们会在这个系列中继续探索DHTMLX Gantt图库的自定义。在本文中我们将考虑一个新的甘特图定制场景&#xff0c;DHTMLX Gantt组件如何创建一个项目路线图。 DHTMLX Gantt正式版下载 用…

系列五、映射文件xxxMapper.xml

一、概述 mapper映射文件是mybatis中最重要的部分&#xff0c;涉及到的细节也非常多。 1.1、parameterType 表示输入参数的类型。例如&#xff1a; <select id"getUserById" parameterType"integer" resultType"org.star.entity.model.UserDO&…

C语言 程序环境 编译和链接

目录 1.程序的翻译环境和执行环境 2.详解C语言程序的编译和链接 2.1翻译环镜 2.2翻译的几个阶段 2.2.1预编译 2.2.2编译 词法分析 符号汇总 2.2.3汇编 生成符号表 2.3链接 1.合并段表 2.合并符号表和重定位 2.4运行环境 1.程序的翻译环境和执行环境 在ANSI C的任…

【Linux】 ps 命令使用

ps &#xff08;英文全拼&#xff1a;process status&#xff09;命令用于显示当前进程的状态&#xff0c;类似于 windows 的任务管理器。 语法 ps [选项] ps命令 -Linux手册页 著者 ps最初由布兰科兰克斯特撰写<lankestefwi.uva.nl>。迈克尔K约翰逊<johnsonmred…

2.Spark的工作与架构原理

概述 目标&#xff1a; spark的工作原理spark数据处理通用流程rdd 什么是rddrdd 的特点 spark架构 spark架构相关进程spark架构原理 spark的工作原理 spark 的工作原理&#xff0c;如下图 图中中间部分是spark集群&#xff0c;也可以是基于 yarn 的&#xff0c;图上可以…

coalesce函数(SQL )

用途&#xff1a; 将控制替换成其他值&#xff1b;返回第一个非空值 表达式 COALESCE是一个函数&#xff0c; (expression_1, expression_2, …,expression_n)依次参考各参数表达式&#xff0c;遇到非null值即停止并返回该值。如果所有的表达式都是空值&#xff0c;最终将返…

Java数据类型

Java数据类型 文章目录 Java数据类型一、基本类型&#xff08;1&#xff09;整数类型&#xff08;2&#xff09;小数类型&#xff08;3&#xff09;真假类型&#xff08;4&#xff09;字符类型 二、数组类型三、逻辑运算1. 三元运算符2.if语句3.Switch语句4. Switch语句case穿透…

4 个最常见的自动化测试挑战及应对措施

有人说&#xff1a;“杂乱无章的自动化只会带来更快的混乱。”不仅更快&#xff0c;而且是更严重、更大的混乱。如果使用得当&#xff0c;自动化可以成为测试团队中令人惊叹的生产力助推器和系统的质量增强器。自动化测试的关键是要正确运用&#xff0c;这是初始最困难的部分。…

大数据毕业设计选题推荐-热门旅游景点数据分析-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【马蹄集】—— 百度之星 2023

百度之星 2023 目录 BD202301 公园⭐BD202302 蛋糕划分⭐⭐⭐BD202303 第五维度⭐⭐ BD202301 公园⭐ 难度&#xff1a;钻石    时间限制&#xff1a;1秒    占用内存&#xff1a;64M 题目描述 今天是六一节&#xff0c;小度去公园玩&#xff0c;公园一共 N N N 个景点&am…

【C++】内联函数一看就懂?

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

JavaEE平台技术——预备知识(Web、Sevlet、Tomcat)

JavaEE平台技术——预备知识&#xff08;Web、Sevlet、Tomcat&#xff09; 1. Web基础知识2. Servlet3. Tomcat并发原理 1. Web基础知识 &#x1f192;&#x1f192;上个CSDN我们讲的是JavaEE的这个渊源&#xff0c;实际上讲了两个小时的历史课&#xff0c;给大家梳理了一下&a…