文档章节

1分钟实现“延迟消息”功能

stars永恒
 stars永恒
发布于 2017/03/19 12:26
字数 997
阅读 50
收藏 2

一、缘起

很多时候,业务有“在一段时间之后,完成一个工作任务”的需求。

 

例如:滴滴打车订单完成后,如果用户一直不评价,48小时后会将自动评价为5星。


一般来说怎么实现这类“48小时后自动评价为5星”需求呢?

 

常见方案:启动一个cron定时任务,每小时跑一次,将完成时间超过48小时的订单取出,置为5星,并把评价状态置为已评价。

假设订单表的结构为:t_order(oid, finish_time, stars, status, …),更具体的,定时任务每隔一个小时会这么做一次:

select oid from t_order where finish_time > 48hours and status=0;

update t_order set stars=5 and status=1 where oid in[…];

如果数据量很大,需要分页查询,分页update,这将会是一个for循环。

 

方案的不足:

(1)轮询效率比较低

(2)每次扫库,已经被执行过记录,仍然会被扫描(只是不会出现在结果集中),有重复计算的嫌疑

(3)时效性不够好,如果每小时轮询一次,最差的情况下,时间误差会达到1小时

(4)如果通过增加cron轮询频率来减少(3)中的时间误差,(1)中轮询低效和(2)中重复计算的问题会进一步凸显

 

如何利用“延时消息”,对于每个任务只触发一次,保证效率的同时保证实时性,是今天要讨论的问题。

 

二、高效延时消息设计与实现

高效延时消息,包含两个重要的数据结构:

(1)环形队列,例如可以创建一个包含3600个slot的环形队列(本质是个数组)

(2)任务集合,环上每一个slot是一个Set<Task>

 

同时,启动一个timer,这个timer每隔1s,在上述环形队列中移动一格,有一个Current Index指针来标识正在检测的slot。

 

Task结构中有两个很重要的属性:

(1)Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务

(2)Task-Function:需要执行的任务指针

假设当前Current Index指向第一格,当有延时消息到达之后,例如希望3610秒之后,触发一个延时消息任务,只需:

(1)计算这个Task应该放在哪一个slot,现在指向1,3610秒之后,应该是第11格,所以这个Task应该放在第11个slot的Set<Task>中

(2)计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1

 

Current Index不停的移动,每秒移动到一个新slot,这个slot中对应的Set<Task>,每个Task看Cycle-Num是不是0:

(1)如果不是0,说明还需要多移动几圈,将Cycle-Num减1

(2)如果是0,说明马上要执行这个Task了,取出Task-Funciton执行(可以用单独的线程来执行Task),并把这个Task从Set<Task>中删除

 

使用了“延时消息”方案之后,“订单48小时后关闭评价”的需求,只需将在订单关闭时,触发一个48小时之后的延时消息即可:

(1)无需再轮询全部订单,效率高

(2)一个订单,任务只执行一次

(3)时效性好,精确到秒(控制timer移动频率可以控制精度)

 

三、总结

环形队列是一个实现“延时消息”的好方法,开源的MQ好像都不支持延迟消息,不妨自己实现一个简易的“延时消息队列”,能解决很多业务问题,并减少很多低效扫库的cron任务。


另外,关于MQ的可达性、幂等性未来撰文另述。

 

如果对文章和配图满意的话,帮忙转发一下哈。

© 著作权归作者所有

stars永恒
粉丝 10
博文 173
码字总数 232942
作品 0
大兴
后端工程师
私信 提问
Spring Boot(十四)RabbitMQ延迟队列

一、前言 延迟队列的使用场景:1.未按时支付的订单,30分钟过期之后取消订单;2.给活跃度比较低的用户间隔N天之后推送消息,提高活跃度;3.过1分钟给新注册会员的用户,发送注册邮件等。 实现...

王磊的博客
2018/11/16
247
0
车江毅/BusinessMQ

##分支说明## 该分支是基于BSF的基础上开发的分支。 修改内容 1)sdk以插件的形式扩展自BSF。 2)项目命名空间从Dyd.BaseService.BusinessMQ修改为BusinessMQ 3) 打包安装包,可以直接被第三...

车江毅
2015/10/12
0
0
免费的午餐——编程利用Google日历API发短信、Email

此外Google还公开了其API,使得我们可以通过各种语言编程访问。 面向.Net的API文档:http://code.google.com/apis/calendar/docs/2.0/developersguide_dotnet.html 开源类库:http://code.g...

老朱教授
2017/11/19
0
0
【RocketMQ】消息的发送

Producer 发送方式 生产者发送消息有三种方式 同步(Sync):发送方线程发送后同步堵塞等待SendResult,若failed则重试下一个broker 异步(Async):发送方线程发送后无需等待返回结果(返回...

SaintTinyBoy
2018/07/29
312
0
深入理解load averages

前言 经常和Linux打交道的童鞋都知道,load averages是衡量机器负载的关键指标,但是这个指标是怎样定义出来的呢? 和其他系统不同,Linux上的load averages不仅追踪可运行的任务,还追踪处于...

大蟒传奇
2018/05/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
7
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
7
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
9
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部