用redis来实现具有ack机制的消息队列

消息队列(MQ)

相信大家对MQ这个词都不会陌生,不管用过还是没用过的,大多会对他有一定的了解,
那么消息队列有什么好处呢

  1. 解耦(接触服务之间的耦合度关系)
  2. 削峰(例如我某个促销活动在某个时间点有非常大的流量涌入,这个时候用Mq做缓存是最好的方式了)
  3. 异步化(例如有些服务是我不需要在同步链中进行调用的,那么可以用mq来做一个异步消费)

传统MQ的缺点

MQ基本上和缓存一样是居家必备之良药。然而消息队列虽然重要,但是同时其实是蛮重的一个组件。例如我们在用rabbitMq的话,我们需要为它搭建一个服务端,如果考虑到可用性,那么我们需要为服务端建立一个集群,同时,我们如果线上问题可能还需要在Mq中做查找,那么这些工作就可能加大我们整体的工作量。

利用redis来实现MQ

所以就想能不能先简单的通过Redis来实现消息队列呢?不考虑PubSub、分布式、持久化、事务等复杂的情况。就像JDK的各种Queue一样。答案当然是可以的,因为Redis提供的list数据结构就非常适合做消息队列。大家可能会发现,网上有很多redis的消息队列,但是目前为止,我没有发现一个消息队列是具有ack机制的。

这里我们会讲述怎么利用list的api中的lpush/brpoplpush来实现一个具有ack机制的消息队列

实现思路

初步实现

实现ack的话,(暂时先不考虑集群版,只是单机版本)

  1. 我可以用lpush做生产者,每次有消息需要生产的时候,就发送一个message到pending队列中。
  2. brpoplpush做消费者,每次取到消息的时候进行业务消费。在消费的同时吧消息放到另一个doing的队列中
  3. 每次消费者完成任务,从doing队列中删除任务msg,用来告知这个消息被成功消费掉了
  4. 然后开一个线程去定时轮询查doing中,如果一定时间(架设我们的message实现了我们的协议,message中带有任务开始的时间戳),这个任务还没被消费成功,那么就把这个doing队列的那个就重新塞到pending的队列里

发现问题

但是这时候可能会出现这样的问题,我轮询doing的队列在取任务的时候可能因为我消费者的任务因为某些原因做的慢了些,那么这时候就会被重新塞会pending队列里,但是过两秒我的doing确实消费完了。

那么怎么解决这个问题呢?

解决方式其实很简单,就是上面的进行步骤3的时候,如果从doing队列进行删除的时候,如果返回值表示删除失败的话,那么说明我们的任务被系统认为过期了,他被赛入pending中了,那么我们只需要在这个时候去pending中重新删除这个message消息即可

延伸问题

ok,那么大家觉得这时候已经完工了吗?其实并没有。。。为什么呢?
因为会出现如下这样一种比较极端的情况:

就是任务完成之后去doing队列中删除message失败,然后去pending中删除也失败,因为有可能在任务扫描的时候,吧任务刚放入pending队列中,没等doing完成呢,pending中重新放入的任务就被消费了。那么这时候依然是消息出现重复

这种情况下的最佳解决方案是什么呢?就是消费端做好幂等性处理(其实像阿里的RocketMq)也会出现消息重复的情况(虽然极低概率),但是在Mq中,似乎设计一个精确只发一次的模型,是一件比较难的事情。

深层延伸的方案

上面的消息重复其实还是有优化的余地,具体的实现思路如下:

  1. 优化扫描的模型,吧扫描doing过期任务变成一个延迟扫描(如用delayedQueue实现延迟任务扫描)
  2. 吧每个执行的任务模型用ExecutorService来管理,存储正在执行的Future
  3. 每次扫描到超时的任务就去内存中查找这个任务的Future是否存在,如果存在则不需要吧doing的message放到pending中
  4. 如果需要超时机制的话,找到对应的Future并且取消当前任务的执行,并把之前执行的操作进行业务回滚/rollback,把message放到pending中

不过我并不推荐这一套方案,因为这一套方案过于复杂,本身就是不是我们用redis作为消息队列的初衷。

总结

redis作为消息队列是有很大的局限性的,本身作为一个以缓存/内存存储为主的东西,只是因为某些api上的特性,我们得以实现一个简单的队列服务,本身我们要选择好业务的取舍,灵活的使用redis的MQ支持,才能实现一个好的服务。

基于上述思想的代码实践我已经放到了github上,部分代码还在做成中。
github地址 : https://github.com/wgd12389/redisses/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能...
    Sophie12138阅读 716评论 0 7
  • “ 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列...
    落羽成霜丶阅读 3,931评论 1 41
  • 女儿明天高考。 为骗自己,连续几天装模作样的去接下夜自习的女儿。之所以这么说是因为耳朵里塞了一对耳机,街边少男倩女...
    方樹阅读 267评论 0 0
  • 周末终于和心心念念的小伙伴处了一天。得知她要去我家,当时的心情简直像充满了气了的气球,飘飘然。一瞬间就在家里...
    南蒲草阅读 279评论 0 0