黑马苍穹外卖技术亮点 详情

news/2024/7/8 1:10:38 标签: spring boot, java, mybatis

1.使用工厂模式和策略模式实现布隆过滤器解决缓存穿透问题

Bitmap

Bitmap是一种数据结构,它使用位图来表示数据。在处理大量数据时,Bitmap可以通过将每个数据元素映射到一个位,然后使用位运算来对数据进行操作。
通过使用Bitmap,我们可以快速地完成排序、查找和去重等操作。优点极大的节省储存空间。但是,Bitmap也存在一些局限性,例如它只能表示有限的数值类型,并且对于数据稀疏的情况可能不太适用。

布隆过滤器

布隆过滤器是一种用于快速检索一个元素是否可能存在于一个集合中的数据结构
它的基本原理是利用多个哈希函数,将一个元素映射成多个位,然后将这些位设置为1。当查询一个元素时,如果这些位都被设置为1,则认为元素可能存在于集合中。布隆过滤器的优点在于它的内存占用极少,并且不局限于数值类型。但是,由于哈希冲突的存在,布隆过滤器也存在误判的可能。

缓存穿透

访问的资源不存在,而导致这个不存在的数据每次请求都要到数据库中去查询,导致数据库挂掉

用布隆过滤器解决缓存穿透问题

不直接访问数据库,而是先通过布隆过滤器判断访问的资源是否存在,存在则放行,不存在则拦截。
使用BitMap作为布隆过滤器,通过多个哈希函数来将被访问的元素映射到位数组中的多个位置上,将对应的位置设置为1。
当需要判断一个元素是否在布隆过滤器中时:只需将该元素进行多次哈希,并检查对应的位数组位置是否都为1,如果其中有任意一位为0,则说明该元素不在集合中;如果所有位都为1,则说明该元素可能在集合中(因为有可能存在哈希冲突),需要进一步检查。

使用工厂模式和策略模式解决缓存穿透问题

  1. 定义布隆过滤器接口:首先定义一个布隆过滤器接口,包括添加元素和判断元素是否存在两个基本操作。
  2. 实现具体的布隆过滤器类:创建类,实现布隆过滤器接口中的方法。在这个类中,需要定义布隆过滗器的数据结构(比如位数组)、大小等属性。
  3. 定义哈希策略接口:定义一个哈希策略接口,包含计算哈希值的方法。
  4. 实现具体的哈希策略类创建多个具体的哈希策略类,每个类对应一种哈希函数的计算方法
  5. 创建布隆过滤器工厂类:定义一个布隆过滤器工厂类,其中包含一个用于创建布隆过滤器对象的工厂方法。工厂方法接受布隆过滤器的大小和哈希策略对象作为参数,并返回一个具体的布隆过滤器对象。
  6. 使用布隆过滤器工厂:在需要创建布隆过滤器对象的地方,调用布隆过滤器工厂的工厂方法来创建布隆过滤器对象,并传入相应的哈希策略对象。
    总结:使用策略模式是创建多个具体的哈希策略类,每个类对应一种哈希函数。并且创建布隆过滤器的工厂类。
    当需要创建布隆过滤器对象时,直接调用工厂类并传入相应的哈希策略对象。
    这样可以将对象的创建和哈希函数的选择解耦,更灵活的创建对象和选择哈希函数。

好处

使用工厂模式和策略模式来实现布隆过滤器带来以下好处:

(1)解耦性:工厂模式和策略模式的结合可以将对象的创建和哈希函数的选择分离,使得各部分之间的耦合度降低。这样在需要修改布隆过滤器的具体实现或者切换哈希函数时,只需要修改相应的工厂类或策略类,而不影响其他部分。
(2)可扩展性:通过工厂模式和策略模式,我们可以方便地添加新的布隆过滤器实现类和哈希函数策略类,而不需要修改现有代码。这样在需要增加新的布隆过滤器类型或者新的哈希函数时,只需添加相应的类即可。

2.使用Redis采用一主两从+哨兵的集群方案

使用Redis

当用户数量较多时,用户频繁的访问数据库,数据库压力增大,系统的性能下降。因此使用Redis对数据进行缓存,减小数据库的压力,提高系统的性能和访问速度。

为什么采用一主两从+哨兵的集群方案(解决高并发高可用问题)

进一步提高Redis的并发能力,可以搭建主从集群,实现读写分离
我们项目的redis采用一主二从的集群方案,主节点负责写数据,从节点负责读数据,主节点写入数据之后,需要把数据同步到从节点中,实现了读写分离,解决了高并发问题
哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送ping命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵可以实现自动故障修复:哨兵可以监测到主节点宕机,自动将从节点切换为主节点并发布通知其他从服务器修改配置文件,当旧主服务器恢复后会成为一个新的从节点。

怎样解决高并发高可用问题

主从集群保证高并发,哨兵保证高可用。

怎样解决数据一致性问题

数据一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。
​使用旁路缓存模式在缓存与数据库双写时保持一致性。
先更新数据库,后删除缓存。理论上会出现不一致的情况,但概率较小因为缓存的写入速度是比数据库的写入速度快很多。

java">**更新缓存替换为删除缓存**,那么**下一次执行的查询操作都会从数据库中加载数据**,此时数据一致性就有了保证。
java">比较方法时注意:缓存被清空后,线程只能到数据库中查询,在数据库中查完就会直接更新缓存。
先查缓存,再查数据库。
**重点在与看结束后数据库与缓存是否一致,用多久一致的。**
java">(1)先删缓存,再更新数据库:
(多线程调度不确定)可能会导致A删缓存,B去查缓存“空”-->然后去查数据库-->拿着数据库中数据更新缓存-->A更新数据库
结束后:数据库中是A更新后的信息,缓存中是原始信息。

(2)先更数据库,再删缓存:
A更数据库,B查缓存-->B拿到原始数据-->A删除缓存
结束后:数据库中新数据,缓存被删即还会获得新数据。【从B在数据库拿到原始数据到写到缓存中,但持续时间相比其他方法短】

(3)延迟双删:
A删缓存,B去查缓存“空”-->然后去查数据库-->拿着数据库中数据更新缓存-->A更新数据库-->延迟删除缓存
结束后:相比1最后缓存与数据库时一致的,但性能上不如2

总结:采取旁路缓存使缓存和数据库保持数据的一致性,
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
更新的时候,先更新数据库,再删除缓存中对应着数据库更新的数据。

3.使用Spring Task实现订单超时取消,使用WebSocket实现用户催单。

WebSocket

基于TCP的一种新的网络协议。实现了浏览器与服务器全双工通信——浏览器和服务器只需完成一次握手,两者就可以建立连接,双向数据传输。
WebSocket:两边都可以主动通信。
应用场景:网页聊天、视频弹幕,股票信息实时更新(服务器主动推送到网页上的)

订单超时取消流程

客户下单后未支付,订单一直处于“待支付”状态。
使用Spring Task工具的cron表达式设置定时任务触发时间,每分钟检查一次是否存在支付超时的订单,如果存在超时则将订单状态修改为“已取消”。

用户催单流程

用户在小程序中点击催单按钮后,根据用户id查询是否有支付成功的订单,有则调用WebSocke实现服务端向客户端推送消息,展示用户订单和支付成功信息。

4.使用自定义拦截器和JWT令牌实现用户登录认证,用ThreadLocal存储用户ID

用户登录认证流程

(1)用户登录时,发送用户名和密码给服务器,服务器验证通过则生成一个包含用户信息的JWT令牌,并将其发送给客户端。
(2)客户端受到JWT令牌后,保存在本地中,
(3)每当用户发起请求时,将JWT令牌添加到请求头中
(4)服务器端的自定义拦截器会拦截所有请求,从请求头中提取JWT令牌。
(5)拦截器解析JWT令牌,获取用户信息,并将其存储在ThreadLocal中,以避免在每次请求时都去解析JWT令牌。
(6)拦截器可以检查用户是否已经通过认证,通过则处理请求,未通过则返回错误信息。
(7)请求完成后,拦截器remove清楚ThreadLocal里的用户信息

为什么用JWT不用Session

传统的Session用户认证方案:

(1)用户向服务器发送用户名和密码。
(2)服务器验证通过后,在当前**对话(session)**里面保存相关数据,比如用户角色、登录时间等等。
(3)服务器向用户返回一个 session_id,写入用户的 Cookie。
(4)用户随后的每一次请求,都会通过 Cookie将 session_id 传回服务器。
(5)服务器收到 session_id,找到对应的session并获取前期保存的数据,由此得知用户的身份。

这种传统的通过session的方式适用于前后端不分离的情况,因为session是保存在服务器端,因此对于跨域或服务器集群的情况很不友好。
而JWT解决了跨域问题,而且(1)jwt基于json,数据处理方便。(2)使用非对称加密和签名技术,安全性高。(3)资源服务使用JWT,可不依赖认证服务即可完成授权。

为什么用ThreadLocal

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
这样可以保证线程之间的数据隔离,避免了线程安全问题。

ThreadLocal使用流程

每次执行请求时拦截器将用户信息用set保存在ThreadLocal中,ThreadLocal每次调用set时是一个独立的线程,当另一个用户调用ThreadLocal的set时方法 时,就会新建另一个线程,线程之间互不影响,当对应线程在调用get时候,就会请求到set时候的信息。在拦截器执行过后的方法中添加ThreadLocal线程remove()方法,这样每次请求结束后会将ThreadLocal线程中的数据删除,这样可以防止线程过多内存泄露。

为什么用拦截器不用过滤器、切面

粒度更细: 拦截器可以针对特定的控制器或控制器方法进行拦截,实现精确的拦截逻辑
而过滤器是基于URL路径进行拦截,无法做到针对具体的控制器或方法,切面也是基于切点进行拦截,粒度相对较粗。

5.使用AOP统一记录登录者操作日志

使用AOP切片,将登陆者增删改用户信息的操作信息记录到数据库中。

流程

(1)建表日志信息表
(2)自定义注解,在增删改等方法运行时调用切面
(3)定义切面类,操作者记录:先通过当前请求的请求头拿到JWT令牌并解析出操作者的用户ID,然后记录操作命令和操作时间等信息插入到日志信息表中。

6.使用Redis分布式锁实现高并发商品秒杀,解决超卖问题

为什么不用单体锁

单体应用难以满足实际高并发访问需求,会将单体应用部署到多个tomcat实例上,由负载均衡将请求分发到不同实例上。
单体锁(synchronized、ReentrantLock)是JVM层面的锁,只能控制单个实例上的并发访问安全,多实例下依然存在数据一致性问题。

分布式锁

分布式锁指的是,所有服务中的所有线程都去获取同一把锁,但只有一个线程可以成功的获得锁,其他没有获得锁的线程必须全部等待,直到持有锁的线程释放锁。

RedLock解决Redis集群主从不同步数据丢失问题

Redisson使用主从集群模式,主节点挂掉,从节点没有同步到锁的情况:
使用RedLock,针对Redis中所有节点来进行同步,能够保证超过半数的Redis加锁了才算加锁成功,从而保证并发安全。

解决流程

多用户并发操作的情况下,多个用户尝试购买同一件商品,导致商品库存不足或者超卖。
采用Redis提供的Redisson组件实现Redis分布式锁,来控制并发访问
当一个线程去获取锁,锁的VALUE中存入UUID来保证锁和当前线程绑定
当前线程获取锁成功开始处理业务时,内部会有watch dog看门狗,每隔10s看当前线程是否还持有锁,如果持有则给锁延长生存时间。

当Redis集群部署时,为了解决主节点挂掉从节点没有同步到锁的情况,使用RedLock,针对Redis中所有节点来进行同步,能够保证超过半数的Redis加锁了才算加锁成功,从而保证并发安全。

另外:用户限流,防止同一用户多次秒杀

使用布隆过滤器记录用户和商品ID来解决。
当用户参与秒杀时,判断是否ID是否记录存在布隆过滤器中,不存在证明该用户是第一次参与秒杀改商品,放行继续后续业务;
过滤器中存在,则禁止继续秒杀。

7.基于Redis使用防重Token和lua脚本,防止重复提交订单

lua脚本作用

使用lua脚本实现原子性的查询并删除操作。为了确保即使多个请求同时到达,也只有一个请求能够成功删除Token。

流程

用户发起请求时,服务端生成唯一的token作为信息凭证
将Token存入Redis并设定过期时间,防止Redis内存溢出。
在后续请求中要携带这个Token,服务器收到后,在Redis中通过lua脚本进行原子性的查询并删除操作
如果Token存在并被成功删除说明是第一次请求则继续处理业务生成订单;如果不存在说明是重复请求,则返回错误提示。


http://www.niftyadmin.cn/n/5535877.html

相关文章

FastAPI教程III

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial 这部分暂无需求的没有记录,仅放置标题。 依赖项 安全性 中间件 你可以向FastAPI应用添加中间件。 ”中间件“是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个…

Python逻辑控制语句 之 判断语句--if语句的基本结构

1.程序执行的三大流程 顺序 分支(判断) 循环 2.if 语句的介绍 单独的 if 语句,就是 “如果 条件成⽴,做什么事” 3.if 语句的语法 if 判断条件: 判断条件成立,执行的代码…

Spring Cloud LoadBalancer基础入门与应用实践

官网地址:https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html 【1】概述 Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器,它包含在SpringCloud-commons中用…

分子AI预测赛笔记

#AI夏令营 #Datawhale #夏令营 Taks1 跑通baseline 根据task1跑通baseline 注册账号 直接注册或登录百度账号,etc fork 项目 零基础入门 Ai 数据挖掘竞赛-速通 Baseline - 飞桨AI Studio星河社区 启动项目 选择运行环境,并点击确定,没…

【双出版加持!录用率高!见刊、检索更稳定!】第六届结构抗震与土木工程研究国际学术会议 (ICSSCER 2024,8月16-18)

随着社会的发展,城市规模的不断扩大,建筑形态也趋于多样化和复杂化,建筑结构形式逐渐由规则简单向高层、大跨甚至特殊复杂的方向发展。而房屋建筑是人们正常生活和生产活动的基本场所,房屋建筑结构的安全必须得到充分保障。但是&a…

ISP IC/FPGA设计-第一部分-SC130GS摄像头分析-IIC通信(1)

1.摄像头模组 SC130GS通过一个引脚(SPI_I2C_MODE)选择使用IIC或SPI配置接口,通过查看摄像头模组的原理图,可知是使用IIC接口; 通过手册可知IIC设备地址通过一个引脚控制,查看摄像头模组的原理图&#xff…

【CSAPP】-linklab实验

目录 实验目的与要求 实验原理与内容 实验步骤 实验设备与软件环境 实验过程与结果(可贴图) 实验总结 实验目的与要求 1.了解链接的基本概念和链接过程所要完成的任务。 2.理解ELF目标代码和目标代码文件的基本概念和基本构成 3.了解ELF可重定位目…

音视频流媒体视频平台LntonAIServer视频监控平台工业排污检测算法

在当今社会,环境保护和可持续发展已成为全球关注的焦点。工业生产作为经济发展的重要支柱,其对环境的影响不容忽视。因此,如何有效地监控和管理工业排污,成为了一个亟待解决的问题。LntonAIServer工业排污检测算法应运而生&#x…