RabbitMQ高阶使用延时任务

news2024/11/26 9:41:55

目录

  • 1 从打车开始说起
  • 1.1 需要解决的问题
    • 1.1.1 打车超时
  • 2 延时任务
    • 2.1 什么是延时任务
      • 2.1.1 和定时任务区别
    • 2.2 延时队列使用场景
    • 2.3 常见方案
      • 2.3.1 数据库轮询
      • 2.3.1 JDK的延迟队列
      • 2.3.3 netty时间轮算法
      • 2.3.4 使用消息队列
    • 2.4 延时队列
      • 2.4.1 TTL(消息过期时间)
    • 2.4.1.1 配置队列TTL
    • 2.5 死信队列
      • 2.5.1 什么是死信队列
      • 2.5.2 死信队列使用场景
      • 2.5.3 死信队列如何使用
      • 2.5.4 相关代码
    • 2.6 打车超时处理
      • 2.6.1 打车超时实现


1 从打车开始说起

在这里插入图片描述

在这里插入图片描述

我们把滴滴打车的流程简化下

在这里插入图片描述

  1. 登录app后点击打车开始进行打车
  2. 打车服务开始为司机派单
  3. 司机接单后开始给来接驾
  4. 上车乘客后处于行程中
  5. 行程结束后完成本次打车服务

1.1 需要解决的问题

我们需要实现派单服务,用户发送打车订单后需要进行进行派单,如果在指定时间内没有找到司机就会收到派单超时的通知,并且能够实时查看当前排队的抢单人数

在这里插入图片描述

下面我们来介绍下涉打车涉及到的一些问题

1.1.1 打车超时

主要讲解打车服务在超时后的处理,比如打车后等待多长时间没有打到车后会通知等待超时

在这里插入图片描述

2 延时任务

我们先看下如何来解决打车超时的问题

2.1 什么是延时任务

在开发中,往往会遇到一些关于延时任务的需求,例如

  • 生成订单30分钟未支付,则自动取消
  • 生成订单60秒后,给用户发短信
  • 滴滴打车订单完成后,如果用户一直不评价,48小时后会将自动评价为5星。

2.1.1 和定时任务区别

对上述的任务,我们给一个专业的名字来形容,那就是延时任务,那么这里就会产生一个问题,这个延时任务定时任务的区别究竟在哪里呢?一共有如下几点区别

  1. 定时任务有明确的触发时间,延时任务没有
  2. 定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
  3. 定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务

2.2 延时队列使用场景

那么什么时候需要用延时队列呢?考虑一下以下场景:

  1. 订单在十分钟之内未支付则自动取消。
  2. 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
  3. 账单在一周内未支付,则自动结算。
  4. 用户注册成功后,如果三天内没有登陆则进行短信提醒。
  5. 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
  6. 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议

可以想一下美团点餐,超时时间

2.3 常见方案

下面我们来介绍下常见的延时任务的解决方案

2.3.1 数据库轮询

​ 该方案通常是在小型项目中使用,即通过一个线程定时的去扫描数据库,通过订单时间来判断是否有超时的订单,然后进行update或delete等操作

优点

​ 简单易行,支持集群操作

缺点

  1. 对服务器内存消耗大
  2. 存在延迟,比如你每隔3分钟扫描一次,那最坏的延迟时间就是3分钟
  3. 假设你的订单有几千万条,每隔几分钟这样扫描一次,数据库损耗极大

2.3.1 JDK的延迟队列

​ 该方案是利用JDK自带的DelayQueue来实现,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入DelayQueue中的对象,是必须实现Delayed接口的。

在这里插入图片描述

优点

​ 效率高,任务触发时间延迟低。

缺点

  1. 服务器重启后,数据全部消失,怕宕机
  2. 集群扩展相当麻烦
  3. 因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常
  4. 代码复杂度较高

2.3.3 netty时间轮算法

时间轮算法可以类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick

在这里插入图片描述

​ 这样可以看出定时轮由个3个重要的属性参数,ticksPerWheel(一轮的tick数),tickDuration(一个tick的持续时间)以及 timeUnit(时间单位),例如当ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和现实中的始终的秒针走动完全类似了。

​ 如果当前指针指在1上面,我有一个任务需要4秒以后执行,那么这个执行的线程回调或者消息将会被放在5上。那如果需要在20秒之后执行怎么办,由于这个环形结构槽数只到8,如果要20秒,指针需要多转2圈,位置是在2圈之后的5上面(20 % 8 + 1)

优点

​ 效率高,任务触发时间延迟时间比delayQueue低,代码复杂度比delayQueue低。

缺点

  • 服务器重启后,数据全部消失,怕宕机
  • 集群扩展相当麻烦
  • 因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常

2.3.4 使用消息队列

在这里插入图片描述

我们可以采用RabbitMQ的延时队列,RabbitMQ具有以下两个特性,可以实现延迟队列

  • RabbitMQ可以针对Queue和Message设置 x-message-ttl,来控制消息的生存时间,如果超时,则消息变为dead letter
  • RabbitMQ的Queue可以配置x-dead-letter-exchangex-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了dead letter,则按照这两个参数重新路由。

优点

​ 高效,可以利用rabbitmq的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性。

缺点

​ 本身的易用度要依赖于RabbitMq的运维,因为要引用RabbitMq,所以复杂度和成本变高

2.4 延时队列

RabbitMQ中没有对消息延迟进行实现,但是我们可以通过TTL以及死信路由来实现消息延迟。

在这里插入图片描述

2.4.1 TTL(消息过期时间)

在介绍延时队列之前,还需要先介绍一下RabbitMQ中的一个高级特性——TTL(Time To Live)

TTL是RabbitMQ中一个消息或者队列的属性,表明一条消息或者该队列中的所有消息的最大存活时间,单位是毫秒,换句话说,如果一条消息设置了TTL属性或者进入了设置TTL属性的队列,那么这条消息如果在TTL设置的时间内没有被消费,则会成为“死信”,如果不设置TTL,表示消息永远不会过期,如果将TTL设置为0,则表示除非此时可以直接投递该消息到消费者,否则该消息将会被丢弃。

2.4.1.1 配置队列TTL

一种是在创建队列的时候设置队列的“x-message-ttl”属性

@Bean
public Queue taxiOverQueue() {
    Map<String, Object> args = new HashMap<>(2);
    args.put("x-message-ttl", 30000);
    return QueueBuilder.durable(TAXI_OVER_QUEUE).withArguments(args).build();
}

这样所有被投递到该队列的消息都最多不会存活超过30s,但是消息会到哪里呢,如果没有任何处理,消息会被丢弃,如果配置有死信队列,超时的消息会被投递到死信队列

2.5 死信队列

讲到延时消息就不能不讲死信队列

2.5.1 什么是死信队列

先从概念解释上搞清楚这个定义,死信,顾名思义就是无法被消费的消息,字面意思可以这样理解

​ 一般来说,producer将消息投递到broker或者直接到queue里了,consumer从queue取出消息进行消费,但某些时候由于特定的原因导致queue中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信,自然就有了死信队列;

2.5.2 死信队列使用场景

RabbitMQ中的死信交换器(dead letter exchange)可以接收下面三种场景中的消息:

  • 消费者对消息使用了basicReject或者basicNack回复,并且requeue参数设置为false,即不再将该消息重新在消费者间进行投递
  • 消息在队列中超时,RabbitMQ可以在单个消息或者队列中设置TTL属性
  • 队列中的消息已经超过其设置的最大消息个数

2.5.3 死信队列如何使用

​ 死信交换器不是默认的设置,这里是被投递消息被拒绝后的一个可选行为,是在创建队列的时进行声明的,往往用在对问题消息的诊断上。

​ 死信交换器仍然只是一个普通的交换器,创建时并没有特别要求和操作,在创建队列的时候,声明该交换器将用作保存被拒绝的消息即可,相关的参数是x-dead-letter-exchange

在这里插入图片描述

2.5.4 相关代码

@Bean
public Queue taxiOverQueue() {
    Map<String, Object> args = new HashMap<>(2);
    // x-dead-letter-exchange    这里声明当前队列绑定的死信交换机
    args.put("x-dead-letter-exchange", TAXI_DEAD_QUEUE_EXCHANGE);
    // x-dead-letter-routing-key  这里声明当前队列的死信路由key
    args.put("x-dead-letter-routing-key", TAXI_DEAD_KEY);
    return QueueBuilder.durable(TAXI_OVER_QUEUE).withArguments(args).build();
}

2.6 打车超时处理

用户通过调用打车服务将数据放进RabbitMQ的死信队列进行延时操作,等待一段时间后,正常的业务处理还没有处理到我们发起的数据,将会进行超时处理,通过通知服务将我们的处理结构通过websocket方式推送到我们的客户端。

在这里插入图片描述

2.6.1 打车超时实现

在创建队列的时候配置死信交换器并设置队列的“x-message-ttl”属性

@Bean
public Queue taxiDeadQueue() {
    return new Queue(TAXI_DEAD_QUEUE,true);
}

@Bean
public Queue taxiOverQueue() {
    Map<String, Object> args = new HashMap<>(2);
    // x-dead-letter-exchange    这里声明当前队列绑定的死信交换机
    args.put("x-dead-letter-exchange", TAXI_DEAD_QUEUE_EXCHANGE);
    // x-dead-letter-routing-key  这里声明当前队列的死信路由key
    args.put("x-dead-letter-routing-key", TAXI_DEAD_KEY);
    // x-message-ttl  声明队列的TTL
    args.put("x-message-ttl", 30000);
    return QueueBuilder.durable(TAXI_OVER_QUEUE).withArguments(args).build();
}

这样所有被投递到该队列的消息都最多不会存活超过30s,超时后的消息会被投递到死信交换器

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

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

相关文章

第七十五天学习记录:高等数学:定积分(宋浩板书)

定积分是微积分中的一个重要概念&#xff0c;表示在给定区间上函数曲线下的面积或有向曲线与坐标轴围成的面积。定积分通常用符号 ∫ 来表示&#xff0c;具体形式为 ∫f(x) dx。 对于给定的函数 f(x) 和区间 [a, b]&#xff0c;定积分的计算可以通过求函数 f(x) 在该区间上的原…

【C++】STL的vector容器

目录 2、vector容器 1.1模板实例化 1.2定义与初始化vector对象 2.1vector构造函数 2.2vector赋值操作 2.2vector的容量和大小 2.4vector的插入 2.5vector的删除 2.6vector数据存取 2.7vector互换容器 2.8vector预留空间 2、vector容器 vector是C最常用的容器之一&a…

深度学习(神经网络)

文章目录 神经网络历史形式神经元模型&#xff08;M-P模型&#xff09;感知器多层感知器 误差反向传播算法误差函数和激活函数误差函数二次代价函数交叉熵代价函数 激活函数sigmoid函数RELU函数 似然函数softmax函数 随机梯度下降法批量学习方法在线学习小批量梯度下降法 学习率…

<Linux开发>驱动开发 -之-阻塞、非阻塞IO和异步通知

&#xff1c;Linux开发&#xff1e;驱动开发 -之-阻塞、非阻塞IO和异步通知 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移…

easyui01(基本布局)

一.概述 1.What&#xff1f; jQuery EasyUI是一组基于jQuery的UI插件集合体&#xff0c;能帮助web开发者更轻松的打造出功能丰富并且美观的UI界面 2.Why&#xff1f; ①.使用easyui 不需要写很多代码&#xff0c;只需要编写一些简单 HTML 标记&#xff0c;就可以定义用户界…

Java优先级队列源码分析

先导课程&#xff1a;二叉堆学习 优先级队列 1. Priority Queue 优先级队列&#xff08;Priority Queue&#xff09;也是队列 普通队列按照FIFO原则&#xff0c;也就是先进先出优先级队列按照优先级高低进行出队&#xff0c;比如将优先级最高的元素作为队头优先出队 基本接口和…

Vue3中Composition 其他一些API

一、 Reactive判断的API 1. isProxy 检查对象是否由reactive或者readonly创建的proxy &#xff0c;返回一个布尔值 <script setup> import { reactive, readonly, isProxy } from vuelet foo readonly({ name: WFT1 }) // 其中的属性不可修改let bar reactive({ n…

DeepSpeed结合Megatron-LM训练GPT2模型笔记(上)

文章目录 0x0. 前言0x1. Megatron使用单卡训练GPT2依赖安装准备训练数据训练详细流程和踩坑 0x2. Megatron使用单卡预测训练好的GPT2模型0x3. 参数量和显存估计参数量估计训练显存占用估计 0x4. Megatron使用多卡训练GPT2模型2卡数据并行2卡模型并行 0x5. 总结 0x0. 前言 本文…

【V4L2】 v4l2框架分析之v4l2_subdev

文章目录 一、v4l2_subdev简介二、初始化v4l2_subdev三、注册/注销subdev四、异步注册子设备 一、v4l2_subdev简介 相关源码文件&#xff1a; /include/media/v4l2-subdev.h/drivers/media/v4l2-core/v4l2-subdev.c 在linux内核中&#xff0c;许多驱动程序需要与子设备通信&…

【嵌入式linux】spi驱动加载后probe函数未执行的问题

【嵌入式linux】spi驱动加载后probe函数未执行的问题 问题描述解决办法 问题描述 嵌入式linux平台下的spi分为设备、总线和驱动&#xff0c;一般半导体原厂已经实现好了spi设备和总线的相关代码&#xff0c;开发者只需根据实际使用情况修改设备树以及编写驱动部分的代码即可。…

Android进阶 四大组件的工作过程(四):ContentProvider的工作过程

Android进阶 四大组件的工作工程&#xff08;四&#xff09;&#xff1a;ContentProvider的工作过程 导语 本篇是介绍四大组件的最后一篇文章&#xff0c;前三篇文章里我们已经介绍了Activity&#xff0c;Service以及Broadcast的工作流程&#xff0c;那么这篇文章我们就来介绍…

【数据结构与算法分析】一文搞定插入排序、交换排序、简单选择排序、合并排序的代码实现并给出详细讲解

文章目录 排序相关的基本概念排序算法及其实现插入排序直接插入排序折半插入排序希尔排序 交换排序冒泡排序快速排序 合并排序归并排序简单选择排序 算法比较 排序相关的基本概念 排序&#xff1a;将数组中所有元素按照某一顺序(从小到大或从大到小)重新排列的过程。排序算法的…

DJ2-5 内容分发网络 CDN

目录 单一的大规模数据中心 内容分发网络 CDN 单一的大规模数据中心 存在三个问题&#xff1a; ① 如果客户远离数据中心&#xff0c;服务器到客户的分组将跨越许多通信链路并很可能通过许多 ISP&#xff0c;给用户带来恼人的时延。 ② 流行的视频很可能经过相同的通信链路…

[C++11] 智能指针

长路漫漫&#xff0c;唯剑作伴。 目录 长路漫漫&#xff0c;唯剑作伴。 为什么需要智能指针 RAII 使用RAII思想管理内存 重载 * 和-> 总结一下智能指针的原理&#xff1a; C的智能指针和拷贝问题 auto_ptr (C98) ​编辑 auto_ptr的实现原理…

EmGUCV中类函数 FastFeatureDetector使用详解

FastFeatureDetector Class 释义&#xff1a;FAST&#xff08;加速检测特&#xff09;关键点检测器&#xff0c;源自 E. Rosten ("Machine learning for high-speed corner detection, 2006). 继承关系&#xff1a;Emgu.CV.Features2D.FastFeatureDetector 派生&#xff…

记录好项目D5

记录好项目 你好呀&#xff0c;这里是我专门记录一下从某些地方收集起来的项目&#xff0c;对项目修改&#xff0c;进行添砖加瓦&#xff0c;变成自己的闪亮项目。修修补补也可以成为毕设哦 本次的项目是 商品信息管理系统 技术栈&#xff1a;SpringBoot Mybatis Thymelea…

MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性

\ &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭…

细谈容器化技术实现原理--以Docker为例

目录 一、Docker解决了什么 二、容器的发展过程 三、容器基础 3.1. 容器实现的原理&#xff1a; ⚠️原理详解&#xff1a; 3.1.1. Namespace 3.1.2. Cgroups 3.1.3. chroot 四、Volume 4.1. Docker是如何做到把一个宿主机上的目录或者文件&#xff0c;挂载到容器里面…

4. 数组更新检测

4.1 v-for更新监测 当v-for遍历的目标结构改变, Vue触发v-for的更新 情况1: 数组翻转 情况2: 数组截取 情况3: 更新值 口诀: 数组变更方法, 就会导致v-for更新, 页面更新 数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set() 这些方法会触发数组改…

基于深度学习的高精度鸽子检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度鸽子检测识别系统可用于日常生活中或野外来检测与定位鸽子目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的鸽子目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…