【黑马点评Redis——003优惠券秒杀4——消息队列Stream】

1. 目前还存在的问题

  • 设置的阻塞队列可能会超出最大长度
  • 系统重启会导致阻塞队列中的信息消失,可能会出现问题

2. 消息队列

  • 消息队列 (Message Queue)。
    • 字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色消息队列:存储和管理消息,也被称为消息代理 (Message Broker)
  • 生产者
    • 发送消息到消息队列
  • 消费者
    • 从消息队列获取消息并处理消息

在这里插入图片描述

  • Redis提供了三种不同的方式来实现消息队列
  • list结构:基于List结构模拟消息队列
  • PubSub:基本的点对点消息队列
  • Stream:比较完善的消息队列模型

2.1 基于List结构模拟消息队列

在这里插入图片描述
优点

  • 利用Redis存储,不受限于JVM内存上限
  • 基于Redis的持久化机制,数据安全性有保证
  • 可以满足消息有序性

缺点

  • 无法避免消息丢失(消息拿出来后没来得及处理就挂了,导致这条消息没有处理)
  • 只支持单消费者(一个人获取了这条消息,另一个人就不能获取了)

2.2 PubSub基本的点对点消息队列

在这里插入图片描述
优点

  • 采用发布订阅模型,支持多生产,多消费

缺点

  • 不支持数据持久化(服务一旦关闭就消失了)
  • 无法避免消息丢失(没人接收这条消息就丢了)
  • 消息堆积有上限,超出时消息丢失(缓存在客户端,有上限)

2.3 基于Stream的消息队列(重点)

Redis 中的 Stream 是一种在 Redis 5.0 版本引入的新数据类型,它专为实现高性能、高可靠性的消息队列和流式数据处理而设计。Stream 结构提供了一种有序、可持久化、可重复消费且支持多路写入与多消费者并行消费的消息存储模型。

写入命令XADD

XADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...]
  • key:消息队列的名称
  • NOMKSTREAM:如果队列不存在,是否自动创建队列。默认是自动创建
  • MAXLEN:最大长度,默认不设置上限
  • *|ID:消息的唯一id,*代表由Redis自动生成。
  • field value [field value …]:称为一个Entry,格式是多个key-value键值对
    在这里插入图片描述
    读取命令XREAD
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

特点

  • 消息可回溯
  • 一个消息可以被多个消费者读取
  • 可以阻塞读取
  • 有消息漏读的风险

2.4 基于Stream的消息队列-消费者组

消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备以下特点:

  • 消息分流
    • 队列中的消息会分流给组内的不同消费者,而不是重复消费,从而加快消息处理的速度
  • 消息标识
    • 消费者者会维护一个标识,记录最后一个被处理的消息,哪怕消费者宕机重启,还会从标识之后读取消息,确保每一个消息都会被消费。
  • 消息确认
    • 消费者获取消息后没消息处于pending状态,并存入一个pending-list。当处理完成后需要通过XACK来确认消息,标记消息为已处理,才会从pending-list移除

2.4.1 创建消费者组

在这里插入图片描述

2.4.2 从消费者组读取消息

在这里插入图片描述

2.4.3 确认消息命令

将pending-list中的某个消息,标记为已处理。

XACK KEY GROUP ID

小结

  • 消息可回溯
  • 可以多消费者争抢消息,加快消息速度
  • 可以阻塞读取
  • 没有消息漏读的风险
  • 有消息确认机制,保证消息至少被消息一次

2.4.4 XPENDING

XPENDING 是 Redis 5.0 引入的一项新命令,用于管理 Redis Streams 中的待处理消息。Redis Streams 是一个用于处理实时数据流的数据结构,而 XPENDING 则允许你查看、管理待处理消息的信息。
XPENDING 命令的一般语法如下:

XPENDING stream_name group_name [start end count] [consumer]

其中:
stream_name 是待处理消息所在的流的名称。
group_name 是消费者组的名称。
start 和 end 是两个可选参数,用于指定待处理消息的范围。
count 是一个可选参数,用于指定要返回的待处理消息的数量。
consumer 是一个可选参数,用于指定特定的消费者。

2.5 Java的伪代码

消费者监听消息的基本思路

while(true){//尝试监听队列,使用阻塞模式,最长等待2000msObejct msg = redis.call("XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAM s1 >");if(msg == null){// null说明没有消息,继续下一次continue;}try{//处理消息,完成后需要ACKhandleMessage(msg)}catch(Exception e){while(true){Object msg = redis.call("XREADGROUP GROUP g1 c1 COUNT 1 STREAM s1 0");if(msg == null){//null说明没有异常消息,所有消息都已确认,结束循环break;}try{//处理消息,完成后需要ACKhandleMessage(msg)}catch(Exception e){//再次出现异常,记录日志,继续循环continue;}}}
}

2.6 对比

在这里插入图片描述

3.代码优化目标

在这里插入图片描述

3.1 修改Lua脚本

-- 1.参数列表
-- 1.1. 优惠券id
local voucherId = ARGV[1]
-- 1.2. 用户id
local userId = ARGV[2]
-- 1.3. 订单id
local orderId = ARGV[3]
-- 2.数据key
-- 2.1.库存key
local stockKey = 'seckill:stock:' .. voucherId
-- 2.2.订单key
local orderKey = 'seckill:order:' .. voucherId-- 3.脚本业务
-- 3.1.判断库存是否充足
if(tonumber(redis.call('get',stockKey))<=0)then-- 3.2.库存不足,返回1return 1
end-- 3.3.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call("sismember",orderKey,userId) == 1) then-- 3.4.存在,说明是重复下单return 2
end-- 3.5.扣库存 incrby stockKey -1
redis.call('incrby',stockKey,-1)
-- 3.6.下单(保存用户)sadd orderKey userId
redis.call('sadd',orderKey,userId)
-- 3.7.发送消息到队列中, XADD stream.order * K1 v1 K2 v2 ...
redis.call('xadd','stream.orders','*',"userId",userId,"voucherId",voucherId,"id",orderId)
return 0

3.2 修改Java端代码

    private class VoucherOrderHandler implements Runnable{String queueName = "stream.orders";@Overridepublic void run(){while (true){try {// 1.获取消息队列中的订单信息List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),StreamOffset.create(queueName, ReadOffset.lastConsumed()));// 2.判断消息获取是否成功if ( list==null || list.isEmpty()){// 2.1.如果获取失败,说明没有消息,继续下一次循环continue;}// 3.解析消息中的订单信息MapRecord<String, Object, Object> record = list.get(0);VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(record.getValue(), new VoucherOrder(), true);// 4.如果获取成功,可以下单handleVoucherOrder(voucherOrder);// 5.ACK确认stringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());}catch (Exception e){handlePendingList();log.error("处理订单异常:",e);}}}private void handlePendingList() {while (true){try {// 1.获取pending-list队列中的订单信息,XREADGROUP GROUP g1 c1 COUNT 1 STREAMS streams.order 0List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(Consumer.from("g1", "c1"),StreamReadOptions.empty().count(1),StreamOffset.create(queueName, ReadOffset.from("0")));// 2.判断消息获取是否成功if ( list==null || list.isEmpty()){// 2.1.如果获取失败,说明没有消息,结束循环break;}// 3.解析消息中的订单信息MapRecord<String, Object, Object> record = list.get(0);VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(record.getValue(), new VoucherOrder(), true);// 4.如果获取成功,可以下单handleVoucherOrder(voucherOrder);// 5.ACK确认stringRedisTemplate.opsForStream().acknowledge(queueName,"g1",record.getId());}catch (Exception e){log.error("处理订单异常:",e);try {Thread.sleep(100);} catch (InterruptedException ex) {throw new RuntimeException(ex);}}}}}

4. 总结

目前我们已经能够使用Stream消息队列来实现一个功能较为完全的秒杀功能。

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

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

相关文章

甲醛传感器ETO-A1在建筑装修过程中甲醛监测的重要作用

随着建筑装修行业的快速发展&#xff0c;甲醛污染问题逐渐受到人们的关注。甲醛是一种常见的室内空气污染物&#xff0c;主要来源于建筑装修过程中使用的各种材料。为了保障人们的健康和安全&#xff0c;甲醛传感器在装修过程中的监测作用显得尤为重要。英国Alphasense公司推出…

矽塔SA6288Q栅极驱动器,可替代峰绍FD6288Q

SA6288 是一款集成了三个独立半桥栅极驱动器&#xff0c;特别适合于三相电机应用中高速功率MOSFET 和 IGBT 的栅极驱动。可在高达250V 电压下工作。 SA6288内置 VCC 和 VBS 欠压&#xff08; UVLO &#xff09;保护功能&#xff0c;防止功率管在过低的电压下工作&#xff0c;…

记一次 Java 应用内存泄漏的定位过程

问题现象 最近&#xff0c;笔者负责测试的某个算法模块机器出现大量报警&#xff0c;报警表现为机器CPU持续高占用。该算法模块是一个优化算法&#xff0c;本身就是CPU密集型应用&#xff0c;一开始怀疑可能是算法在正常运算&#xff0c;但很快这种猜测就被推翻&#xff1a;同…

springboot基于点餐码 二维码在线点餐系统vue.js+java

Maven: 项目管理和构建自动化工具&#xff0c;用于java项目。 java: 广泛使用的编程语言&#xff0c;适用于构建跨平台应用。 Springmvc:从而在使用Spring进行WEB开发时&#xff0c;可以选择使用Spring的Spring MVC框架。 MyBatis: java持久层框架&#xff0c;支持定制化SQL、存…

第68天:APP攻防-XposedFridaHook证书校验反代理代理转发

目录 思维导图 案例一&#xff1a;某牛防抓包-xposed&frida&r0capture 如何检测是否启动了反代理 xp框架 方案二&#xff1a;某社交防抓包-Proxifier&frida&r0capture 思维导图 案例一&#xff1a;某牛防抓包-xposed&frida&r0capture 这里某牛软…

Python | Leetcode Python题解之第46题全排列

题目&#xff1a; 题解&#xff1a; class Solution:def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""def backtrack(first 0):# 所有数都填完了if first n: res.append(nums[:])for i in range(first, n):# 动…

WebSocket的原理、作用、API、常见注解和生命周期的简单介绍,附带SpringBoot示例

文章目录 原理作用客户端 API服务端 API生命周期常见注解SpringBoot示例 WebSocket是一种 通信协议 &#xff0c;它在 客户端和服务器之间建立了一个双向通信的网络连接 。WebSocket是一种基于TCP连接上进行 全双工通信 的 协议 。 WebSocket允许客户端和服务器在 单个TCP连接上…

基于FPGA轻松玩转AI

启动人工智能应用从来没有像现在这样容易&#xff01;受益于像Xilinx Zynq UltraScale MPSoC 这样的FPGA&#xff0c;AI现在也可以离线使用或在边缘部署、使用.可用于开发和部署用于实时推理的机器学习应用&#xff0c;因此将AI集成到应用中变得轻而易举。图像检测或分类、模式…

Python写个二维码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、进入官网下载二、下载一下三.输入代码 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、进入官网下载 官网 pip insta…

vue3推荐算法

Vue 3 推荐算法主要指的是在 Vue 3 框架中实现的或者适用于 Vue 3 的算法库或组件库。Vue 3 由于其优秀的设计和性能&#xff0c;被广泛应用于构建各种类型的应用程序&#xff0c;包括需要复杂算法支持的项目。以下是一些在 Vue 3 中可能会用到的推荐算法资源&#xff1a; Vue-…

田忌赛马【洛谷P1650】

P1650 田忌赛马 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<iostream> #include <algorithm> #include<cstdio> #include <map> using namespace std; const int N1e5100; int n; map<int,int>a,b;//映射&#xff0c;速度->数量…

新网站上线需要注意什么?

质量保证&#xff1a;确保网站的所有功能和页面都经过了充分的测试&#xff0c;并且在各种不同的浏览器和设备上都能够正常运行。检查所有链接、表单和交互式元素&#xff0c;确保它们都能够按照预期工作。优化性能&#xff1a;确保网站加载速度快&#xff0c;响应迅速。优化图…

Python-VBA函数之旅-isinstance函数

目录 一、isinstance函数的常见应用场景&#xff1a; 二、isinstance函数使用注意事项&#xff1a; 三、如何用好isinstance函数&#xff1f; 1、isinstance函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff…

基于spring boot学生综合测评系统

基于spring boot学生综合测评系统设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件…

typedef 定义函数指针

typdef int(*FUNC_TYPE)(int,int) FUNC_TYPE p NULL; 定义了一个函数指针 函数指针作为函数的参数的用法demon

黄金行情下跌有投资机会吗?

尽管黄金价格的波动常常引起投资者的高度关注&#xff0c;但行情的下跌未必只是警讯&#xff0c;亦可能蕴藏着某些难得的投资机会。总之&#xff0c;答案是肯定的——在黄金行情下跌时&#xff0c;依旧有适宜的投资机会&#xff0c;只是这需要投资者具备相应的应对知识和策略。…

美森快船和以星快船有什么区别?美线海运都有哪些快船?

在繁忙的国际海运市场中&#xff0c;快船服务以其高效、快捷的特点受到广大货主的青睐。其中&#xff0c;美森快船和以星快船作为知名的海运服务提供商&#xff0c;凭借着卓越的服务品质&#xff0c;在航运界树立了良好的口碑。那么&#xff0c;美森快船和以星快船究竟有何不同…

利用ollama和open-webui本地部署通义千问Qwen1.5-7B-Chat模型

目录 1 安装ollama 2 安装open-webui 2.1 镜像下载 3 配置ollama的模型转换工具环境 3.1 下载ollama源码 3.2 下载ollama子模块 3.3 创建ollama虚拟环境 3.4 安装依赖 3.5 编译量化工具 7 创建ollama模型 8 运行模型 参考文献&#xff1a; 1 安装ollama curl -fsSL …

2-2 任务:闰年判断

本次课&#xff0c;我们讨论了闰年的判断方法、关系运算符与关系表达式、逻辑运算符与逻辑表达式&#xff0c;以及流程控制结构中的选择结构。 闰年判断 闰年是为了使日历年与地球绕太阳公转的时间保持一致而设定的&#xff0c;具有366天。闰年的判断规则如下&#xff1a; 普…

16V/2A高集成功率MOS同步降压转换器SOT23-6封装

概述&#xff1a; PCD8020 是一款内部集成两个功率 MOS 的高效率 2A 同步整流降压转换器。 该器件提供PWM 与 PFM 两种控制模式&#xff0c; 能够在很宽的负载范围内实现高效率。PCD8020 采用小巧的 SOT23-6 封装&#xff0c; 外围器件少&#xff0c; 从而实现小尺寸的系统电源…