2024年9月21日发(作者:)
第37卷 第1期
2021年1月
福 建 电 脑
Journal of Fujian Computer
Vol. 37 No.1
Jan. 2021
互联网延迟队列解决方案设计
李祎 王宏渊 彭金喜
(广州大学华软软件学院 广州 510990)
摘 要 针对互联网中广泛存在的延迟任务处理问题,本文分析了技术难点和常见的解决方案,并基于Redis中间件设计和
实现了延迟队列系统。该系统具有轻量级、高可用、低耦合、高性能等特点,能够支撑业务系统的稳定运行。
关键词 延迟任务;延迟队列;Redis
中图法分类号 TP311 DOI:10.16707/.2021.01.003
The Solutions for the Internet Delay Task Processing Problem
LI Yi, WANG Hongyuan, PENG jinxi
(South China Institute of Software Engineering, Guangzhou University, Guangzhou, China, 510990)
Abstract In view of the widespread problem of delay task processing in the Internet, this paper analyzes the
technical difficulties and common solutions, and designs and implements a delay queue system based on redis
middleware. The system has the characteristics of lightweight, high availability, low coupling and high
performance, which can support the stable operation of the business system.
Keywords Delay Task; Delay Queue; Redis
1引言
随着近年来移动互联网的飞速发展,业务环境
越来越复杂,许多互联网项目经常面临着以下几种
业务场景:
(1)在订单系统中,某订单一直处于未支付
状态,如何及时关闭订单,并返还库存。
(2)订单完成以后,如果用户一直未评价,7
天后系统自动好评。
(3)在会员系统中,如何在会员到期3天前
发送续费短信通知提醒。
上述业务场景的共同特点是:业务逻辑并不直
接通过用户前端事件触发,也不能通过系统设定的
统一时间点触发,业务的执行时间取决于用户前一
段时间的行为,按一定的要求延迟执行。例如,在
订单系统中,如果用户在11点下订单且30分钟未
———————————————
支付,则可以考虑在11:30执行关闭订单业务逻
辑。在会员系统中,如果用户订阅了1个月会员,
则可以考虑在订阅当天的27天以后执行续费通知
业务逻辑。
此类业务问题,通常需要采用延迟队列来解
决。针对互联网中广泛存在的延迟任务处理问题,
本文分析了技术难点和常见的解决方案,并研制了
一个实现延迟队列系统。
2解决方案分析
2.1基于编程语言的解决方案
使用编程语言自带的定时器、队列等组件可以
通过定时器实现延迟队列。在Java语言中,通过其
自带的Timer或DelayQueue组件就可以完成延迟队
列
[1]
,在其它语言生态中也有类似的库和组件可以
使用。这种解决方案的优点是架构简单,可通过编
本文得到广州大学华软软件学院科学研究项目(201716)资助。李祎(通信作者),男,1991年生,硕士,主要研究领域为消息中间件设计、软
件工程。E-mail:***************。王宏渊,男,1972年生,硕士,讲师,主要研究领域为软件测试、计算机教育。E-mail:************.cn。彭金
喜,男,1982年生,硕士,主要研究领域为图像处理、模式识别、机器学习。E-mail:************.cn。
10 李祎等:互联网延迟队列解决方案设计 第1期
码直接控制业务逻辑,无需其他中间件支持即可完
成延迟队列。但这种解决方案仅限于单机环境下使
用,在分布式环境下则无法使用。
2.2
基于数据库的解决方案
将任务写入数据库,定期扫描数据库表,发现
任务到期则开始执行对应的任务。例如,针对订单
系统,定期扫描支付数据表,发现有超时未支付的
数据则关闭对应订单。这种解决方案本质上是一种
轮询解决方案,通过不断轮询数据库表,找到需要
延迟执行的任务并执行。这种解决方案的优点仍然
是架构简单,只需要数据库即可完成,无需引入其
他中间件支持。但轮询需要保持与数据库长时间连
接,对数据库占用高。当数据表较大时,轮询所需
要的时间也会变长,有可能出现规定时间内业务处
理不完的情况。这会导致延迟任务无法按时执行。
2.3基于消息中间件的解决方案
一些消息队列中间件自带延迟消息组件,如
RocketMQ
、
RabbitMQ
、
Beanstalk
等消息队列均支
持延迟队列功能。通过此类消息中间件提供的
API
可以完成延迟队列
[2]
。这种方案是目前较为成熟的
延迟队列解决方案,基本可以满足大多数延迟队列
使用场景
[3]
。但使用这种方案需要额外引入消息中
间件,有的消息中间件延迟队列方案本身也比较复
杂,已有业务接入消息队列比较困难,会增加业务
系统的复杂度。
2.4自研延迟队列解决方案
利用定时器、队列、网络通信等原理,结合以
上方案的优点和自身业务系统的特点自研延迟队
列解决方案
[4]
。自研延迟队列解决方案应满足架构
简单、低耦合、不侵入已有业务系统等要求。本文
将详细介绍一种自研延迟队列设计方案。
3延迟队列方案设计
3.1设计目标
自研延迟队列应满足以下设计目标
[5]
:
(1)轻量级:尽量不使用第三方扩展,减少
第三方中间件的引入。
(2)高可用:支持多实例部署,一个实例出
错不影响整个服务正常运行。
(3)低耦合:尽可能不侵入已有业务系统,
降低系统之间的耦合性。
(4)高性能:每秒处理请求数量(QPS)要尽
可能高,QPS至少在1000以上。
3.2 架构设计
设计延迟队列需要解决以下几个问题:如何计
算和保存任务的执行时间;如何处理延迟任务执行
的先后顺序;如何在对应的执行时间完成延迟任务
调用;如何在调用延迟任务时获取对应的参数;如
何处理延迟任务并发的业务场景;如何处理延迟任
务失败的业务场景等。
为了解决延迟队列设计问题,同时满足设计目
标,在综合考察多种设计方案后
[6-8]
,发现Redis中
间件可以比较优雅地解决上述问题。Redis是一个
远程字典服务中间件,提供了列表、哈希、集合、
有序集合等多种数据结构,常用于数据库、缓存、
消息队列
[9]
,具有非常高的性能和可用性。利用
Redis提供的有序集合不仅可以保存延迟任务的任
务编号,同时可以保存延迟任务的执行时间,并可
以对所有保存的延迟任务按照执行时间进行排序;
利用Redis的哈希类型保存延迟任务的编号和任务
参数等详细信息;利用Redis提供的阻塞列表可以
模拟队列操作,使延迟任务按照入队列的顺序有序
执行
[10]
。利用上述特性结合网络编程完成整体架构
设计。整体架构设计图如图1所示。
图1 延迟队列架构设计图
延时队列大致分为五大组件。
(
1
)任务池(
Job Pool
):采用
Redis
的哈希类
型存储,用来保存延迟队列任务的详细信息。其中
每个任务以主题和
ID
作为键,以业务参数作为值。
当需要执行某个任务时,可以根据主题和
ID
获取
该任务对应的参数数据。
(
2
)延迟任务桶(
Delay Bucket
):采用
Redis
的有序集合类型存储,用来保存延迟任务及其执行
时间,集合当中保存每个延迟任务的主题和
ID
,并
将任务延迟执行时间作为集合元素分数保存。
(
3
)定时器(
Timer
):负责定时扫描延迟任务
2021年 福 建 电 脑 11
桶,在扫描过程中,如果发现延迟执行时间小于等
于当前时间的延迟任务则将其放到对应的延迟任
务队列中。
(
4
)延迟任务队列(
Delay Queue
):采用
Redis
的列表类型存储,用来模拟延迟队列,在队列中保
存的是当前需要被消费的延迟任务
ID
。
(
5
)消费者(
Consumer
):任务消费者监听对
应主题的延迟任务队列,从延迟任务队列中取出待
执行的任务,并从任务池中取出该任务的详细参
数,执行对应的回调逻辑,完成延迟任务的执行。
3.3生命周期
延迟队列生命周期如图2所示,分为以下四步。
图2 延迟队列生命周期示意图
(1)生产者新增一个延迟任务,会在任务池
中插入一条数据,记录延迟任务详细参数;同时会
在延迟任务桶中插入一条数据,记录延迟任务执行
的时间戳。
(2)定时器定时扫描延迟任务桶,查找执行
时间比当前时间小的任务,将这些记录全部删除;
同时解析出这些任务的主题,将这些任务添加到对
应主题的延迟任务队列中。
(3)每个主题的延迟任务队列都有一个监听
程序负责监听队列中的待消费数据,获取到的数据
均提交到对应的任务消费者。
(4)消费者会根据得到的主题和任务ID数据
查找任务池获取延迟任务参数,并根据回调地址和
回调参数执行对应的回调方法。
通过以上4步就完成了延迟任务的生产和消
费。从生命周期中可以发现:利用Redis中间件设
计的延迟队列可以较好地完成任务的生产、注册和
消费,对原有业务代码几乎没有侵入性,可以单独
配置、部署和扩展。
3.4实现细节
(1)任务池设计分析
任务池用于保存延迟任务的详细信息。需要建
立任务ID和任务参数的对应关系,以便于通过任
务ID查询任务详细信息。因此采用Redis中的哈希
类型进行保存。其中Field部分保存主题和任务ID,
Value部分保存任务内容,在任务内容中采用JSON
格式序列化存储参数信息。任务池设计如表1所示。
表1 任务池数据结构设计
Field Value
{TOPIC}:{JOB_ID} JSON_CONTENT1
{TOPIC}:{JOB_ID} JSON_CONTENT2
当产生延迟任务时,首先根据业务类型确定任
务主题(TOPIC),再按照一定的规则(通常为顺序
递增规则)确定任务ID(JOB_ID);再将此任务的
参数信息按照JSON格式进行序列化得到任务内容
(JSON_CONTENT);最后将对应的键值对保存到
任务池结构当中,完成延迟任务信息保存。
(2)延迟任务桶设计分析
延迟任务桶用于保存延迟任务对应的执行时
间。需要建立任务ID和任务执行时间的对应关系
并按照执行时间排序,定时器进程可以从延迟任务
桶中按照任务执行时间顺序取出任务。该模块是延
迟队列的关键,采用Redis的有序集合结构可以同
时保存任务ID和对应任务的执行时间,并支持集
合按照执行时间进行排序。延迟任务桶设计如表2
所示。
表2 延迟任务桶数据结构设计
Member Score
{TOPIC}:{JOB_ID} DELAY_TIME1
{TOPIC}:{JOB_ID} DELAY_TIME2
当产生延迟任务时,首先根据业务类型确定主
题(TOPIC),再按照一定的规则确定任务ID
(JOB_ID);再根据当前时间和延迟执行的时间计
算延迟任务预期执行时间,这样就确定了延迟执行
时间(DELEY_TIME);最后将任务ID作为集合元
素,将延迟执行时间作为集合元素的分数写入有序
集合当中,有序集合会按照集合元素的分数进行排
序,这样就实现了延迟任务按执行时间进行排序。
(3)定时器设计分析
定时器是一个定时扫描进程,定时器扫描延迟
任务桶按任务执行时间顺序从中获取延迟任务,并
判断延迟任务的执行时间是否小于等于当前时间,
如果满足条件则说明延迟任务的执行时间已到,此
时从延迟任务桶中删除对应的元素,并根据取出的
12 李祎等:互联网延迟队列解决方案设计 第1期
任务主题将延迟任务添加到延迟任务队列模块对
应主题的队列当中。
(4)延迟任务队列设计分析
延迟任务队列用于模拟任务队列,采用Redis
的列表类型实现。通过Redis列表的LPUSH、RPOP
操作可以模拟先进先出的任务队列。在延迟任务队
列模块中,每个主题都对应一个任务队列,整个延
迟任务队列模块由多个主题对应的多条任务队列
组成。延迟任务队列的结构如表3所示。
表3 延迟任务队列数据结构设计
列表名称 列表内容示例
TOPIC1 [JOB_ID1,JOB_ID2,JOB_ID3, ...]
TOPIC2 [JOB_ID1, JOB_ID4, ...]
TOPICN [JOB_IDN, ...]
定时器模块从延迟任务桶中取出符合条件的
延迟任务,并根据延迟任务的主题(TOPIC)将对
应的延迟任务ID(JOB_ID)通过LPUSH操作添加
到对应的延迟任务队列当中。在每条任务队列当
中,都保存了当前等待执行的延迟任务ID。
(5)任务消费者设计分析
表4 任务消费者模块业务逻辑伪代码
# 从延迟队列中取出延迟任务ID
JOB_ID = (TOPIC_NAME)
# 从任务池JOB_POOL中取出延迟任务参数
JOB_PARAMS = (JOB_POOL, TOPIC_NAME + ":" + JOB_ID)
# 向回调地址发送请求完成延迟任务
result = (CALLBACK_URL, JOB_PARAMS)
# 根据回调结果做不同处理,若执行失败间隔一段时间再执行
if result == TRUE:
return "OK"
else:
DELAY_TIME = CURRENT_TIME + DELAY_TIMESPAN
(DELAY_BUCKET, [TOPIC_NAME + ":" + JOB_ID,
DELAY_TIME])
return "DELAY"
任务消费者由不同主题对应的若干个消费者
进程组成,每个消费者进程都订阅其对应主题的任
务队列。当对应的任务队列有任务时,通过BRPOP
操作取出对应的延迟任务,并根据主题和延迟任务
ID信息从任务池中查询任务参数,在获取到对应的
延迟任务参数后,通过消费者模块配置的回调地址
完成延迟任务服务的调用。若回调执行成功则延迟
任务结束,若回调执行失败,则将延迟任务再添加
到任务池和延迟任务桶中,等待延后再次执行。任
务消费者模块业务逻辑用伪代码描述如表4所示。
4 设计总结
经过系统分析、设计、编码、测试等软件开发
过程,自研延迟队列系统基本满足了最初的设计目
标,具体而言:
(
1
)轻量级:系统只依赖
Redis
中间件,不依
赖其它组件,而
Redis
本身非常轻量。
(
2
)高可用:系统采用主从架构保证服务层
高可用,
Redis
集群和哨兵机制则保证了数据层高
可用
[11]
。
(
3
)低耦合:延迟队列不侵入原有业务系统,
原有业务系统对接延迟队列几乎不需要修改。
(
4
)高性能:
Redis
内存数据库特性保证了系
统高性能,能够支持
1000
以上
QPS.
该系统经过部署和优化,目前已应用于学院微
信点餐平台等内部系统当中,接入的业务主题包
括:订单
30
分钟不支付自动取消、优惠券到期
24
小时前提醒等。从目前的使用情况来看,服务总体
运行稳定,各项设计指标也符合预期,没有出现过
大的故障,业务系统基本可以保持流畅运行。
参 考 文 献
[1] 孙弋,温迅.一种面向消息的中间件的设计与实现.物联网技术,
2019,9(3):81-84
[2] 鱼朝伟,詹舒波.基于RabbitMQ的异步全双工消息总线的实现.软件,
2016, 37(02):139-146
[3] 吴璨,王小宁,肖海力,等.分布式消息系统研究综述.计算机科学,
2019(B06):1-5
[4] 邵鹏,彭章友.基于微服务的消息中间件设计与实现.工业控制计算
机,2019,32(04):112-113,115
[5] George Coulouris,等.分布式系统:概念与设计.第5版.北京:机械工业
出版社, 2013
[6] 有赞延迟队列设计. /queuing_delay/,2016,3,22
[7] 夏振华.延迟队列系统DelayQue的设计与实现[硕士学位论文].南京
大学,南京,2015
[8] 符璨.一种基于Redis的轻量级消息中间件的设计与实现[硕士学位论
文].北京大学,北京, 2013
[9] 李子骅. Redis入门指南.第2版.北京:人民邮电出版社, 2015
[10] Josiah L. Carlson. Redis实战.北京:人民邮电出版社, 2015
[11] 黄健宏. Redis设计与实现.北京:机械工业出版社, 2014
本文发布于:2024-09-21 18:58:15,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/1726916295435598.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |