Feign实现微服务间远程调用续;基于Redis实现消息队列用于延迟任务的处理,Redis分布式锁的实现;(黑马头条Day05)

目录

延迟任务和定时任务

使用Redis设计延迟队列原理

点评项目中选用list和zset两种数据结构进行实现

如何缓解Redis内存的压力同时保证Redis中任务能够被正确消费不丢失

系统流程设计

使用Feign实现微服务间的任务消费以及文章自动审核

系统微服务功能介绍

提交文章->审核文章执行流程

Redis中SET NX实现分布式锁


延迟任务和定时任务

定时任务

        有固定周期,有明确的触发事件。

延迟任务

        没有固定的开始时间,它常常是由一个事件触发的,而在这个事件触发之后的一段时间内触发另一个事件,任务可以立即执行,也可以延迟一段时间后执行。参考如下:


        延迟任务的实现常常基于一个延迟队列,延迟队列的实现方案有:

        DelayQueue、RebbitMQ、Redis中基于Zset数据结构的实现。【本篇文章主要介绍项目中使用到的Redis实现的延迟队列,后续会将其他方法实现的延迟队列逐步完善总结】

使用Redis设计延迟队列原理

        Redis的基本数据结构中的Zset内部可以根据给定的权重对元素进行排序,随后使用

 stringRedisTemplate.opsForZSet().rangeByScore(key, min, max),对指定的Key寻找score在min-max间的元素。在向Zset中插入元素的时候可以将优先级设置为socre如果将时间作为优先级实现延迟队列,可以在插入元素同时获取当前系统时间作为socre,如果需要指定5min后执行,则将当前系统获取的时间+5min作为对应元素的socre值。实现基于Redis作为延迟队列。

点评项目中选用list和zset两种数据结构进行实现

        常规需求下基于Redis实现的延迟队列,只需要根据zset设置对应元素的score即可实现,如果进一步考虑数据量非常大的情况下此时时间复杂度比较高。在zset中分别使用zadd(.)以及zrange(.)的时间复杂度分别为:

  • ZADD时间复杂度O(M*logN):M成功添加的元素数,N是有序集合的基数。
  • ZRANGE:按照从低到高的顺序,获取指定排名范围内的成员。时间复杂度:O(log(N)+M),其中 N 是有序集合的基数,M 是指定排名范围内的成员数量。

        选用list和zset相结合的方式实现延迟队列,list中存储当前需要执行的任务,zset中存储需要延迟(未来执行)的任务此时向list的一端存储元素并从list的另一端取出元素,不仅可以保证任务消费的有序性,同时list中存储以及获取元素的时间复杂度均为O(1)在数据量大的情况下性能更优。 

如何缓解Redis内存的压力同时保证Redis中任务能够被正确消费不丢失

        Redis是基于内存的数据库,有一定的存储容量,可以采用Redis+MySQL相结合的方式。

  • 每次到达一个新的任务需要延迟消费时,首先将对应任务存储到MySQL数据中,其次将其根据消费时间(立马消费、延迟消费)存储到对应list或zset中。
  • 在任务被消费时,首先从Redis的list中获取元素进行消费,并将任务从Redis中删除,同时将对应的任务从MySQL数据库中进行删除,避免重复消费。
  • 任务需要消费时首先将其存储到MySQL中,随后将对应时间范围内(比如小于当前时间5min)存入到Redis中,时间大于规定范围的存储到MySQL数据库中,并且每消费一条Redis中的任务同时将MySQL中对应的任务清理。所以MySQL中存储的任务均是未消费的任务,使用定时任务从MySQL中提取任务并加载到Redis中进行消费,此操作必须先将Redis中的任务全部清空,避免相同的任务再次加载到Redis中被重复消费。
  • zset中存储的任务借助Spring Task框架提供的定时任务功能,按照一定时间间隔自动根据score提取对应范围的任务并将其加载到list中进行消费。

系统流程设计

使用Feign实现微服务间的任务消费以及文章自动审核

系统微服务功能介绍

  • ①:feign微服务:定义feign远程调用的接口。
  • ②:article微服务:app端数据存储,以及实现feign中定义的保存文章配置相关接口。
  • ③:schedule微服务:消息队列微服务,实现任务MySQL的记录以及Redis中任务的消费。同时实现feign中定义的调用延迟队列的接口。
  • ④:wemedia微服务:浏览器端/管理端实现,用于实现保存自媒体文章,调用sehedule微服务,实现任务延迟消费以及调用article微服务实现文章自动审核后保存app端文章相关信息。

为什么需要将延迟队列相关实现单独防止在一个微服务schedule中:

        提高复用性,如果将延迟队列实现防止在wemedia微服务中,直接进行调用可以省去不必要的远程调用过程或者MQ实现。同时出现如果其他微服务也需要使用到Redis实现的消息队列,此时需要重新实现,所以将其抽取为一个单独的微服务,提高复用性。

提交文章->审核文章执行流程

        可以参考SpringCloud Feign实现微服务间的远程调用(黑马头条Day04)-CSDN博客 的了解Feign的远程调用的简单原理。

  1. 自媒体发布文章,远程调用消息队列微服务,将任务存入消息(延迟)队列。
  2. 自媒体微服务通过远程调用定时拉取消息队列中的任务进行文章审核。
  3. 自媒体微服务审核完文章后调用app端相关微服务,将文章相关html页面对应的url路径等信息存入到文章相关数据表。

上图中有两个地方并没有画出:

  • 延迟队列微服务定期从zset中根据score范围取数据并放进list中进行消费。
  • 延迟队列微服务定期从MySQL数据库中加载未消费的任务到延迟队列。

贴两个小代码:

    /*** 定时刷新数据从ZSet到list中*/@Scheduled(cron = "0 */1 * * * ?")public void refresh(){// 添加分布式锁String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);if(StringUtils.isNotBlank(token)){log.info("启动定时刷新任务,当前时间为:{}", System.currentTimeMillis() / 1000);// 获取所有未来数据的集合的keySet<String> fututrKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");for (String fututrKey : fututrKeys) {// 根据futureKey计算topicKeyString topicKey = ScheduleConstants.TOPIC + fututrKey.split(ScheduleConstants.FUTURE)[1];// 获取该组key下需要消费的数据Set<String> tasks = cacheService.zRangeByScore(fututrKey, 0, System.currentTimeMillis());// 将需要消费的任务添加list中if(!tasks.isEmpty()){cacheService.refreshWithPipeline(fututrKey, topicKey, tasks);log.info("成功的将{}对应的数据刷新到{}中", fututrKey, topicKey);}}}}/*** 定时加载数据库中的数据到Redis中*/@PostConstruct  // 开启即加载@Scheduled(cron = "0 */5 * * * ?")public void reloadData(){// 清理缓存中的数据clearCache();// 查询数据库中数据,根据执行时间小于当前时间5min// 获取5分钟后的时间Calendar calendar = Calendar.getInstance();calendar.add(Calendar.MINUTE, 5);List<Taskinfo> taskinfos = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTime()));if(taskinfos != null && taskinfos.size() > 0){// 将查询的数据添加到缓存中for (Taskinfo taskinfo : taskinfos) {Task task = new Task();BeanUtils.copyProperties(taskinfo, task);task.setExecuteTime(taskinfo.getExecuteTime().getTime());addTaskToRedis(task);log.info("添加任务到Redis中:{}", task);}}}/*** 清理缓存中的数据*/private void clearCache() {Set<String> topicKey = cacheService.scan(ScheduleConstants.TOPIC + "*");Set<String> futureKey = cacheService.scan(ScheduleConstants.FUTURE + "*");cacheService.delete(topicKey);cacheService.delete(futureKey);}

Redis中SET NX实现分布式锁

        为什么需要分布式锁:控制分布式系统有序的去对共享资源进行操作,通过互斥来保证数据的一致性。考虑以下场景,如果两个延迟队列微服务同时从zset中刷新未来要执行的任务到list中,由于两个微服务设置的定时时间都一样,此时会出现共享变量的重复操作。

         使用Redis实现的分布式锁保证同一时刻只有一个微服务操作共享资源。

sexnx (SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

这种加锁的思路是,如果 key 不存在则为 key 设置 value,如果 key 已存在则 SETNX 命令不做任何操作

  • 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功

  • 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败

  • 客户端A执行代码完成,删除锁

  • 客户端B在等待一段时间后再去请求设置key的值,设置成功

  • 客户端B执行代码完成,删除锁

可以参考Redission实现的分布式锁:Redis分布式锁实现-CSDN博客。

暂时写到这里,有时间再补.....

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

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

相关文章

zookeeper Study

zk介绍&#xff1b;一种分布式协调服务。 分布式锁&#xff0c;集群选举&#xff0c;数据同步 。 zk都能进行操作&#xff0c;redis&#xff0c;kafka&#xff0c;rabbitmq&#xff0c;都能够用zk做协调管理服务。关键时zk简单操作。 应用说明&#xff1a; 简单介绍一下流程 &…

Python 3 教程(2)

Python3 基础语法 编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*- 上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&…

02-gitlab的数据备份和恢复

一、gitlab的数据备份 关于数据备份&#xff0c;咱们就不需要多说什么了&#xff0c;主要就是方式数据意外丢失&#xff0c;导致代码上线流程及数据的损坏崩溃&#xff1b; 为了避免严重生产事故&#xff0c;进而gitlab有了数据备份与恢复的功能。 1&#xff0c;修改gitlab的配…

悬浮工具球(仿 iphone 辅助触控)

悬浮工具球&#xff08;仿 iphone 辅助触控&#xff09; 兼容移动端 touch 事件点击元素以外位置收起解决鼠标抬起触发元素的点击事件问题 Demo Github <template><divref"FloatingBal"class"floating_ball":class"[dragging, isClick]&q…

C语言中的UTF-8编码转换处理

C语言UTF-8编码的转换 1.C语言简介2.什么是UTF-8编码&#xff1f;2.1 UTF-8编码特点&#xff1a; 3.C语言中的UTF-8编码转换处理步骤1&#xff1a;获取UTF-8编码的字节流步骤2&#xff1a;解析UTF-8编码步骤3&#xff1a;Unicode码点转换为汉字 4.总结 1.C语言简介 C语言是一门…

【Unity InputSystem】实用指南:在PC端(鼠标与键盘)、手机端(触摸屏)、主机手柄上同步实现角色移动与跳跃功能

前引 随着Unity的不断发展&#xff0c;开发者对于项目的输入系统要求也日益提高。在进行多平台适配和跨平台移植时&#xff0c;常常需要改变输入系统&#xff0c;这给开发者带来了不少困扰。而Unity官方推出的InputSystem插件&#xff0c;则是为了解决这一问题而推出的全新输入…

ChatGPT 控制机器人的基本框架

过去的一年&#xff0c;OpenAI的chatGPT将自然语言的大型语言模型&#xff08;LLM&#xff09;推向了公众的视野&#xff0c;人工智能AI如一夜春风吹遍了巴黎&#xff0c;全世界都为AI而疯狂。 OpenAI ChatGPT是一个使用人类反馈进行微调的预训练生成文本模型。不像以前的模型主…

2024 年中国高校大数据挑战赛赛题 D:行业职业技术培训能力评价完整思路以及源代码分享

中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴 熟的技术工人进入工厂。某行业在全国有多所不同类型&#xff08;如国家级、 省级等&#xff09;的职业技术培训学校&#xff0c;进行 5 种技能培训。学员入校时需要 进行统一的技能考核&#xf…

Pb量级超大容量光存储

近日&#xff0c;中国科学院上海光学精密机械研究所&#xff08;以下简称“上海光机所”&#xff09;与上海理工大学等科研单位合作&#xff0c;在超大容量三维超分辨光存储研究中取得突破性进展。研究团队利用国际首创的双光束调控聚集诱导发光超分辨光存储技术&#xff0c;实…

unity学习(53)——选择角色界面--分配服务器返回的信息

好久没写客户端了&#xff0c;一上手还不太适应 1.经过测试&#xff0c;成功登陆后&#xff0c;客户端请求list_request&#xff0c;成功返回&#xff0c;如下图&#xff1a; 可见此时model第三个位置的参数是1.也成功返回了所有已注册角色的信息。 2.之前已知创建的角色信息…

CentOS Linux - Primavera P6EPPM安装部署

引言 根据计划&#xff0c;近期我制作了多套基于ORACLE Primavera P6 最新发布的23.12版本预构建了虚拟机环境&#xff0c;里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机&#xff0c;请先与Oracle Primavera销售代表取得…

【Linux】第四十一站:线程控制

一、Linux线程VS进程 1.进程和线程 进程是资源分配的基本单位线程是调度的基本单位线程共享进程数据&#xff0c;但也拥有自己的一部分数据:线程ID一组寄存器&#xff08;上下文&#xff09;栈errno信号屏蔽字调度优先级 2.进程的多个线程共享 同一地址空间,因此Text Segment、…