【SpringCloud】企业认证、分布式事务,分布式锁方案落地-1

目录

HR企业入驻

 HR企业入驻 - 认证流程解析

 HR企业入驻 - 查询企业是否存在

 HR企业入驻 - 上传企业logo与营业执照

HR企业入驻 - 新企业(数据字典与行业tree结构解析)

行业tree 

行业tree - 创建节点

行业tree - 查询一级分类

行业tree - 查询子分类列表

 行业tree - 修改分类

行业tree - 删除分类

 HR企业入驻 - 业务松耦合原则

 HR企业入驻 - 自连接多表查询

结合Redis提升接口QPS

DB数据修改并重置Redis

缓存双删原理解析

缓存不一致的问题出现

双删生活小实例

延迟队列 - 缓存弱一致性

 延迟队列 - 插件安装与配置

延迟队列 - 发送并监听延迟消息

延迟队列 - 延时更新缓存

定时任务

作业:优化全量缓存同步


HR企业入驻

 HR企业入驻 - 认证流程解析

 HR企业入驻 - 查询企业是否存在

涉及页面

根据企业状态判断

接口开发

controller:

service:

如果企业不存在,则跳转到创建新公司页面:

@PostMapping("uploadLogo")
public GraceJSONResult uploadLogo(@RequestParam("file") MultipartFile file) throws Exception {// 获得文件原始名称String filename = file.getOriginalFilename();if (StringUtils.isBlank(filename)) {return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_NULL_ERROR);}filename = "company/logo/" + filename;MinIOUtils.uploadFile(minIOConfig.getBucketName(), filename, file.getInputStream());String imageUrl = MinIOUtils.uploadFile(minIOConfig.getBucketName(),filename,file.getInputStream(),true);return GraceJSONResult.ok(imageUrl);
}

上传营业执照

@PostMapping("uploadBizLicense")
public GraceJSONResult uploadBizLicense(@RequestParam("file") MultipartFile file) throws Exception {// 获得文件原始名称String filename = file.getOriginalFilename();if (StringUtils.isBlank(filename)) {return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_NULL_ERROR);}filename = "company/bizLicense/" + filename;String imageUrl = MinIOUtils.uploadFile(minIOConfig.getBucketName(),filename,file.getInputStream(),true);return GraceJSONResult.ok(imageUrl);
}

思考

为啥上传接口不统一作为一个独立接口去提供给所有上传组件使用呢?

HR企业入驻 - 新企业(数据字典与行业tree结构解析)

跳转到选择行业与人员数量:

数据字典与枚举的区别:

  • 数据字典是可以人物可控的,可以删除或新增,可以显示或者不显示;
  • 枚举是固定的,有多少值就是多少,如果要改需要修改代码并且重启服务。像:男女性别,是否,则可以作为枚举会更好。

行业tree 

行业tree - 创建节点

前端代码

后端代码

在网关中添加路由:

把行业service和mapper从逆向工具中拷贝到项目中:

service:

再去前端测试即可。

行业tree - 查询一级分类

前端代码

后端代码

controller:

service:

行业tree - 查询子分类列表

前端代码

后端代码

 行业tree - 修改分类

前端代码

后端代码

 

行业tree - 删除分类

前端代码

后端代码

 HR企业入驻 - 业务松耦合原则

一级列表

 HR企业入驻 - 自连接多表查询

三级列表

controller:

service:

创建自定义mapper:

结合Redis提升接口QPS

顶级列表

先从redis中查询,如果没有,再从db查询并且放入redis中。

别忘记加配置:

三级列表

DB数据修改并重置Redis

增删改:一级节点

一旦对行业进行增删改,那么必须进行对redis中的已有数据进行删除或修改。

一级节点增删改,则删除Redis中现有所有的TOPINDUSTRYLIST。 在对应的增删改3个接口中增加如下:

增删改:三级节点

service:

sql脚本:

SELECT top.id from industry third
RIGHT JOIN industry `second` 
ON third.father_id = `second`.id
RIGHT JOIN industry top 
ON `second`.father_id = top.id
WHERE third.id = '1539849596215492610'

mapper:

在controller中新增一个方法去重置redis的行业list即可:

说明

为行业添加高性能缓存机制: 当前页面虽然比不过主页,但是这也是包含在主要业务中的,发布职位或者企业相关都会涉及到。

此外,我们这里不要做强关联,什么意思呢。比如把电子商务改成电商直播,用户下次修改不应该显示电商直播,只能显示为老的数据,我们不能去改,所以这就是强弱关联。用户如果要修改就修改为新的即可。

如果我们强制让他显示电商直播,那么他对别的用户来说显示就不够好了,我们并没有通知他,但是却修改了显示,所以不能做强关联。不仅仅是行业,其实很多类似的KV显示,都不能做强关联。

缓存双删原理解析

直接删除所带来的危害

容易缓存雪崩,一开始没有缓存,如果这个时候有高并发流量进来,瞬时会打中数据库,导致数据库崩溃

所以删除完毕之后,需要重新把数据设置到redis中。

缓存不一致的问题出现

按照上面的方式做了,那么有没有可能会出现其他问题呢?

这里需要进行查询并且重新覆盖,如果查询后在覆盖的同时,app端发起的请求,正好恰巧也查询到原来的脏数据,则会直接覆盖,如此导致两边的数据不一致。

到达箭头位置,正好前端app也请求一次,由于没有缓存,那么是不是前端也会从数据库查询一次呀,但是查询到的数据,可能是脏数据,因为之前的事务可能还没有提交,随后在下面的set新数据以后,被覆盖了旧数据,在某些极端情况下,会出现这样的情况,这个虽然有点钻牛角尖,但是确确实实,有概率会发生。 (我们没有在同一个service中去做,所以这样的概率其实已经规避了,如果是在同一个service里的话,则必定会出现这样的情况)

来,咱们通过下图来演示:

所以,为了确保数据两边一致,我们在存储完毕之后,再从redis中删除一次,那么后续前端app的请求则是最新数据了,如此就是双删,我也见过有的项目是三删,总共删3次。。。为了确保数据一致。

用户更新数据前,先把缓存数据删除,然后更新到数据库,再同步到redis中,哪怕redis存入不成功,那么后续用户发起请求还是可以先查库后存缓存,达到一致性。

缓存双删,用户把新数据保存到数据库后,sleep1秒或半秒后再次删除。再次删除redis中的内容可能是脏数据,如果前端再一次查询,哪怕先执行,那么查询到的也是最新的数据了。这也是一个双保险。

注意:从业务角度分析:并发请求的时候,用户的查询是很多的。如果出现了1-2秒的脏数据缓存,那么显示的数据就会有部分是老数据,但是对于整个系统来讲无所谓,没有太大的影响,而且用户的注意力是在列表上,具体是什么行业分类其实还好,没有太大的影响,因为行业哪怕修改了,相关性还是有的,所以有几秒的不一致是无所谓的,因为热点数据的并发读是很大的,一旦删除,那么这个时候由于缓存击穿,数据库可能会瞬间被炸了,直接宕机。所以务必以系统可用性为优先考虑。

阿里的内部规范,是可以允许存在脏数据的,因为哪怕有脏数据,也要保证数据库正常运作不被打死。因为数据库死了,必定有资损,会亏钱,脏数据不一致了,不会导致资损的,所以系统设计务必以高可用性为优先考虑。

双删生活小实例

延迟队列 - 缓存弱一致性

大家有没有觉得现在这种缓存更新方式太麻烦了?全靠代码控制,太复杂了?

又或者说,我们有没有这么必要有这么强的一致性让前端用户去获得,如果获得的是老数据又怎样?大不了用户以后修改呗,对系统毫无影响。用户端展示的企业行业只不过是老的数据,以后可以改啊。

我们来看一下弱一致性的表现:

所以说我们当前的场景完全可以使用弱一致性来做,因为本身在之前运营修改的前几天,只要有用户使用行业,则必定在当前运营修改之后,他们的行业就变成了老数据,所以这完全是可以行的,现在到第二天凌晨更新一下,也顶多是多了1天的用户老数据,系统是完全可以容忍的。

所以说大家在学习老师的课程的时候,我一直在强调业务场景,业务是非常重要的,我们一定要根据业务去做技术,不要为了技术而技术,不能太死板,要灵活取巧。

 延迟队列 - 插件安装与配置

进入rabbitmq控制台:

docker exec -it rabbitmq bash

查看mq的插件列表

rabbitmq-plugins list

以下这个位置,如果安装好延迟队列的插件,会出现,现没有。则需要下载并配置安装

前往如下地址并且下载延迟插件: https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

可以根据如下命令得到mq的版本号,根据版本号去下载延迟插件

没有3.9.11那么下载3.9.0也是可以的:

下载后并且拖入到linux中:

从虚拟机拷贝到docker的rabbitmq插件中:

docker cp rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq:/plugins

再次进入到rabbitmq控制台,可以查看到插件已经存在:

运行如下命令开启延迟插件:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

再次查看插件列表,ok~

rabbitmq-plugins list

ctrl+d退出控制台并且重启rabbitmq:

docker restart rabbitmq

延迟队列 - 发送并监听延迟消息

创建一个接口

前端调用代码

增加一个按钮:

<el-button icon="el-icon-upload" size="mini" type="success" @click="doRefreshIndustry">刷新缓存</el-button>

增加接口调用api:

export function refreshIndustry() {return request({url: '/industry/refreshIndustry',method: 'post'})
}

导入接口api:

增加按钮点击事件:

doRefreshIndustry() {refreshIndustry().then(response => {var data = response.data;console.log(data);this.$notify({title: "刷新成功",message: "最新行业数据将在第二天被刷新~~",type: "success",duration: 2000,});});
},

后端处理代码

延迟MQ配置类 复制一个MQ配置类,取名为如下,并且修改交换机和队列名称

交换机需要设置延迟特性:

修改绑定名与路由key:

添加一个消息属性处理器,目的是设置延迟的时间:

接口调用

@Autowired
private RabbitTemplate rabbitTemplate;/*** 调用刷新行业缓存的接口(延迟队列)* @return*/
@PostMapping("refreshIndustry")
public GraceJSONResult refreshIndustry() {// 计算凌晨三点到现在的时间LocalDateTime futureTime = LocalDateUtils.parseLocalDateTime(LocalDateUtils.getTomorrow() + " 03:00:00",LocalDateUtils.DATETIME_PATTERN);// 计算当前时间和凌晨发布的时间差Long publishTimes = LocalDateUtils.getChronoUnitBetween(LocalDateTime.now(),futureTime,ChronoUnit.MILLIS,true);
//        int delayTimes = publishTimes.intValue();int delayTimes = 10*1000;       // 固定时间,用于写死10秒进行延迟的测试// 发送延迟队列MessagePostProcessor processor = DelayConfig_Industry.setDelayTimes(delayTimes);// 发送延迟消息rabbitTemplate.convertAndSend(DelayConfig_Industry.EXCHANGE_DELAY_REFRESH,DelayConfig_Industry.DELAY_REFRESH_INDUSTRY,"123456",processor);return GraceJSONResult.ok();
}

监听延迟消息:

@Slf4j
@Component
public class RabbitMQDelayConsumer_Industry {@RabbitListener(queues = {DelayConfig_Industry.QUEUE_DELAY_REFRESH})public void watchQueue(Message message, Channel channel) throws Exception {String routingKey = message.getMessageProperties().getReceivedRoutingKey();log.info("routingKey = " + routingKey);String msg = new String(message.getBody());log.info("msg = " + msg);log.info("当前时间为:" + LocalDateTime.now());if (routingKey.equalsIgnoreCase(DelayConfig_Industry.DELAY_REFRESH_INDUSTRY)) {log.info("10秒后监听到延迟队列");}channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}
}

测试延迟的时间是否正确即可。

延迟队列 - 延时更新缓存

可以在此测试批量删除。

查询一级分类行业下的所有三级列表:

SELECT third.*,`second`.*,`top`.* FROM industry third 
left JOIN industry `second`
ON third.father_id = `second`.id
left JOIN industry top
ON `second`.father_id = top.id
WHERE third.`level` = 3
-- 优化为如下:
SELECT third.*,`top`.id as topId FROM industry third 
left JOIN industry `second`
ON third.father_id = `second`.id
left JOIN industry top
ON `second`.father_id = top.id
WHERE third.`level` = 3

mapper:

由于查询出来的数据,同一个topId对应多个不同三级行业,所以可以用1对多的关系来构造这个返回对象。可以利用mybatis的resultMap来进行改造为如下:

service:

获得三级列表:

JSON格式转换一下,可以得到一个大list。

循环设置到redis中:

测试如下:

定时任务

能不能用定时任务来做? 可以!但是每天都会定时去查询,这些数据并不是每天都会修改,难得改一下,所以非必要不要查询数据库,况且这些数据还是挺多挺大的。降低数据库被查导致的风险发生。

作业:优化全量缓存同步

  • 分级分类用批量,不要全量查询,修改操作了哪些,则记录id,再操作,降低数据表的查询总量。
  • 不要直接查询全部三级list,上一点提出的id列表,循环后逐个查询,循环去查询进行拼接,把性能放在服务中进行损耗,不要把一个大的sql放在数据库里执行,降低数据库的损耗。
  • 前端刷新控制按钮刷新次数,每天只能3次或者5次。

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

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

相关文章

FOC笔记(一)电角度零点校准

当电机上电时&#xff0c;它处于位置的电角度未知。如果按上图U4&#xff08;100&#xff09;通电&#xff0c;也会让电角度为0,但是这样力量很大。 简单的方法是只控制d角度的磁场大小&#xff0c;转矩磁场q为0,生成一个定向磁场指向电角度为0。 foc->sin_sita 0;foc->…

全国区块链职业技能大赛样题第9套后端源码

后端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746050 前端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746216 智能合约+数据库表设计:https://blog.csdn.net/Qhx20040819/article/details/140746646 项目预览 登录 用户管理

X用户最多的国家排名统计报告

数据为DataReportal发布的Twitter在各个国家的用户数统计。 2022年&#xff0c;Twitter用户最多的国家是美国&#xff0c;有7690万用户。 数据统计单位为&#xff1a;万人 数据说明&#xff1a; 数据截止时间为2022年1月 Twitter在各个国家的用户情况 2022年&#xff0c;Twit…

全球相机控制面板市场展望与未来增长机遇:预计未来六年年复合增长率CAGR为4.3%

在全球摄影器材和专业影像设备需求增长的背景下&#xff0c;相机控制面板正成为市场的焦点。本文详细分析了全球相机控制面板市场的现状、增长趋势及未来前景&#xff0c;旨在为投资者和业内人士提供深入的市场洞察和指导。 市场概览 据恒州诚思团队研究分析显示&#xff0c;2…

自动控制:带死区的PID控制算法

带死区的PID控制算法 在计算机控制系统中&#xff0c;为了避免控制动作过于频繁&#xff0c;消除因频繁动作所引起的振荡&#xff0c;可采用带死区的PID控制。带死区的PID控制通过引入一个死区&#xff0c;使得在误差较小的范围内不进行控制动作&#xff0c;从而减少控制系统的…

13.2 MongoDB

13.2 MongoDB 1. 概述2. docker安装3. SpringBoot整合MongoDB3.1 依赖3.2 配置连接1. 基于`yml`配置2. 基于配置类配置3.3 启动项坑1坑23.4 新增业务1. 实体类映射2. 数据层3. 业务层4. 控制层5. 测试结果3.5 单条记录查询业务1. 数据层2. 业务层3. 控制层4. 断点测试3.6 分页查…

CeoMax总裁主题最新3.8.1破解免授权版/WordPress付费资源素材下载主题

CeoMax总裁主题最新3.8.1破解免授权版&#xff0c;一套WordPress付费资源素材下载的主题&#xff0c;感觉这是做资源站唯一一个可以和ripro媲美甚至超越的模板&#xff0c;UI很美&#xff0c;功能也很强大&#xff0c;有想学习的可下载搭建学习一下&#xff0c;仅供学习研究借鉴…

爬虫-实战爬取虎扑ACG帖子

要求如下: 爬取虎扑步行街 ACG 版面的数据,要求使用多线程来并发爬取。范围是第一页的所有帖子,每个帖子包含标题、主题内容和第一页的所有回复内容。最后打印出爬到的所有帖子的标题。 网址是:ACG圈 - 虎扑社区。 针对上面的要求,我们进行分析: 首先是要使用多线程范…

【iOS】暑期第一周——ZARA app仿写

目录 前言无限轮播图分栏控件和滚动视图自定义cell遇到的问题调整图标大小单元格附件视图设置 总结 前言 暑假学习的第一周任务是对ZARA app进行仿写&#xff0c;充分运用之前学习的Objective-C语言和UI控件。我在编写demo的过程中遇到了一些问题&#xff0c;特写该博客作为学习…

【医疗图像分割】UNETR++论文笔记及代码跑通实践

在医疗图像分割任务中&#xff0c;transformer模型获得了巨大的成功&#xff0c;UNETR提出了efficient paired attention (EPA) 模块&#xff0c;利用了空间和通道注意力来有效地学习通道和空间的特征&#xff0c;该模型在Synapse&#xff0c;BTCV,ACDC,BRaTs数据集上都获得了很…

cf960(div2)

A. Submission Bait&#xff08;博弈&#xff09; 题意&#xff1a;爱丽丝和鲍勃在大小为n的数组a中进行游戏&#xff0c;他们轮流进行运算&#xff0c;爱丽丝先开始&#xff0c;不能运算的一方输&#xff0c;一开始mx0&#xff0c;每次操作&#xff0c;玩家可以选择一个牵引i…

实验1-2 简单求阶乘问题

PTA浙大版《C语言程序设计实验与习题指导&#xff08;第4版&#xff09;》题目集&#xff1a;实验1-2 简单求阶乘问题 #include<stdio.h> int main(){int n;scanf("%d",&n);//此处是输入数值int a,sum1; //a 是循环的次数&#xff1b;sum 是输出数值for(a…

yarn安装electron时报错RequestError:socket hang up

安装electron时候&#xff0c;出现RequestError:socket hang up这样的错误&#xff0c;找了半天很多方式都是用旧淘宝源&#xff0c;导致根本安装不上去。 在项目的根目录下创建.npmrc文件&#xff0c;添加以下内容 # registryhttps://mirrors.huaweicloud.com/repository/np…

Optional类的使用 java8(附代码)

&#x1f370; 个人主页:_小白不加班__ &#x1f35e;文章有不合理的地方请各位大佬指正。 &#x1f349;文章不定期持续更新&#xff0c;如果我的文章对你有帮助➡️ 关注&#x1f64f;&#x1f3fb; 点赞&#x1f44d; 收藏⭐️ 文章目录 一、什么是Optional&#xff1f;二、…

源码拆解SpringBoot的自动配置机制

SpringBoot相比于Spring系列的前作&#xff0c;很大的一个亮点就是将配置进行了简化&#xff0c;引入了自动化配置&#xff0c;仅靠几个注解和yml文件就取代了之前XML的繁琐配置机制&#xff0c;这也是SpringBoot的独有特点&#xff0c;下面我们从源码角度&#xff0c;一点点拆…

【自然语言处理】概论(一):自然语言处理概要

1.1 概论&#xff1a;&#xff08;一&#xff09;自然语言处理概要 知识点 自然语言的定义&#xff1a;人类交流使用的&#xff0c;包括口语和书面语的信息交流方式。AI的终极目标&#xff1a;使计算机具备理解&#xff08;听、读&#xff09;和生成&#xff08;说、写&#…

使用 WebSocket 实现实时聊天

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

PySide(PyQt)使用QPropertyAnimation制作动态界面

主脚本&#xff1a; # encoding: utf-8 import os import sysfrom PySide6.QtCore import QPropertyAnimation, QEasingCurvefrom UIS import *# 主画面类 class MainWindow(QMainWindow, animationButton_ui.Ui_MainWindow):def __init__(self):super().__init__()self.setup…

GitHub狂飙3万star的LLM公开资料 - 大模型入门教程

先用一张图片说明这篇blog多火热&#xff01; 本篇大型语言模型&#xff08;LLM&#xff09;课程分为三个部分&#xff1a; &#x1f9e9; LLM基础&#xff1a;涵盖了数学、Python和神经网络的基本知识。 &#x1f9d1;‍&#x1f52c; LLM科学家&#xff1a;专注于使用最新技…

Spring源码学习笔记之@Async源码

文章目录 一、简介二、异步任务Async的使用方法2.1、第一步、配置类上加EnableAsync注解2.2、第二步、自定义线程池2.2.1、方法一、不配置自定义线程池使用默认线程池2.2.2、方法二、使用AsyncConfigurer指定线程池2.2.3、方法三、使用自定义的线程池Excutor2.2.4、方法四、使用…