Redis 队列

队列的实现

举例:
队列主要用在系统解耦、流量削峰、异步处理、数据顺序处理等场景。新手在使用时可能会犯一些常见的错误。下面讲一个新手容易犯的错误,在这个示例中把队列的入队、出队和Redis存储节点的主从关系给混淆了,示例如下

存储:
Redis主节点M, 使用数据List类型做为队列,列表名称M (标记为M.L,意为主节点上的List)
Redis从节点S, 做为Redis主节点的备,有和主节点相同的列表名称L (标记为S.L,意为从节点上的List

应用程序:
应用名称App

操作:
入队命令: LPUSH M.L Data
出队命令: RPOP S.L

我们知道,Redis主从节点数据的流向是主节点->从节点,队列中一般数据也是队尾(入队)-> 队头(出队),这两个数据流向混淆后就会出现以上的错误。

image.png

上图显然不是我们想要的结果,这种设计导致的问题是Redis主节点使用的内存会不断增长直至触发Redis的LRU策略导致数据丢失或者无法入队。Redis的从节点S仅仅是做为主节点M的一个备份节点存在的。

image.png

阻塞队列

阻塞队列是一种特殊的队列,具体是指对出队动作在队列为空时的阻塞行为以及在有元素入队后对出队的通知行为.我们知道事件通知机制是服务端通过一定的途径向客户端发送事件消息来实现的,服务端和客户端通常保持长链接。生产者(通知方)向队列中发送事件消息,消费者(接收方)从队列中拿走(POP)事件消息,当队列中没有事件消息的时候,消费者(接收方)阻塞,消费者(接收方)和队列之间保持长链接。

消费者(接收方)的代码(Java)逻辑应该大致如下:

    while (true){
            try {
                EventMessage em = client.brpop(list, timeout);
                doSomething(em);
            }catch(Exception e){
                continue;
            }
      }

使用循环是因为每次brpop命令只能拿到一个队列中的元素,为了得到消息的连续性必须循环brpop。在循环中使用try...catch...是防止在brpop的过程中由于网络闪断、连接池等因素导致的连接不可用抛出异常致使循环不可继续不能连续获取事件消息。

生产者(通知方)的Redis命令逻辑大致如下:

MULTI
      some command...
      LPUSH list x
      EXEC

阻塞队列与PUB/SUB在实现事件通知上的区别

  • 1.PUB/SUB方式在消息消费者(被通知方)订阅一个或多个话题后和Redis保持长链接等待通知,之后可以连续的得到事件通知消息;阻塞队列brpop/blpop方式每次只能获取一个事件消息(即便pop多个队列也是一样),客户端需要循环使用brpop/blpop来获取事件消息,直至队列为空阻塞。
  • 2.PUB/SUB方式实现的通知只有在消息消费者(被通知方)和Redis服务有在线连接的情况下才能收到,一旦连接断开消费者(被通知方)将不能收到通知,即便重新成功连接也无法收取丢失的通知消息;而阻塞队列在客户端断开后重新连接成功后可以收到通知消息。
  • 3.PUB/SUB方式事件消息消费者可以有多个并且每个消费者都能得到相同的消息;而阻塞队列虽然事件消费者可以有多个但是消息只是分发给其中一个消费者,消息无法重复消费。

可靠队列

在Redis的列表(List)实现的队列中,一般一个客户端通过LPUSH命令将消息放入队列中,而另一个客户端通过RPOP/BRPOP 命令有顺序的取出队列中的消息进行消费。但是这种方式却不一定安全,因为在这个过程中,一个客户端可能在取出一个消息之后在处理这个消息之前崩溃,而未处理完的消息也就因此而丢失,并且无法找回。在一些希望是更可靠的消息传递系统中的应用上,这可能会导致一些问题。使用 RPOPLPUSH/BRPOPLPUSH 命令可以解决这个问题:因为它不仅返回一个消息,同时还将这个消息添加到另一个备份列表当中,当一个客户端完成某个消息的处理之后,可以从备份表中把消息删除。

image.png

对于备份列表一般有两种设计方式:每个客户端各自私有一个备份列表和所有客户端公用一个备份列表。两种方式实现机制不同,举例如下

    1. 每个客户端各自私有一个备份队列
  • 客户端从队列里获取消息之前首先检查自己的私有备份队列。
  • 如果备份队列中有消息说明这条消息没有处理完,使用RPOPLPUSH/BRPOPLPUSH把这个消息取出来的同时再次放入备份队列(再次放入是防止从备份队列中取出后没处理完丢失),处理消息完成后把备份队列中的消息删除。
  • 如果备份队列中没有消息,从消息队列中使用RPOPLPUSH/BRPOPLPUSH获取消息并把消息放入备份队列,处理完消息后把备份队列中的消息删除。

按照规则形成的处理逻辑,对每一个客户端分别使用自己私有的备份队列从根本上规避了所有客户端公用备份队列中由于“后发先至”等问题导致的复杂逻辑。

image.png
  • 2.所有客户端公用一个备份队列,专门客户端做确认检查

  • 所有客户端从队列中取出消息数据并通过RPOPLPUSH/BRPOPLPUSH放入备份队列

  • 所有客户端处理消息完成后再从队列中取出下一个消息重复以上动作

  • 专门客户端处理备份消息队列校对消息是否做完,如果做完则删除,否则重做消息。专门客户端起到一个补漏的作用。

image.png

旋转队列

在使用RPOPLPUSH命令的时候,它的两个参数如果是相同的队列键,客户端就可以一个接一个的获取从队头到队尾的所有元素并且把获取的元素放置到队尾。我们称作队列的旋转。这种情况在多个客户端对同一个队列进行旋转时也有效。甚至有客户端在旋转时增加队列的元素也是可行的。

此种模型对于队列和客户端来说都是易于扩展的。因为即便客户端获取元素后失败旋转到下一个周期后仍能获取到元素因此是安全的。这个模型使得我们可以很容易实现这样一类系统:有多个客户端,需要连续不断地对一些元素进行周期性的遍历轮训处理。一个典型的例子就是数据定时采集系统:采集点保存在队列中,客户端从队列中拿采集点然后采集数据。


image.png

优先权队列

优先队列和一般队列有所不同,它不完全遵循先进先出的规则,而是根据队列中元素的优先权,优先权最大的先被取出。队列中的元素不仅存储元素本身还存储元素的优先权数据。使用Redis数据结构实现的方式是按照优先权建队列(列表),相同优先权的元素在同一个队列中,客户端在使用BRPOP/RPOP命令使队列中的元素出队的时候参数按照优先权从高到低的顺序进行。

例如,

  • 队列1:包含0~9,10个元素
  • 队列2:包含0~3,4个元素
  • 队列3:包含0~5,6个元素

优先权:队列1>队列2>队列3
客户端使用命令为:RPOP/BRPOP 队列1 队列2 队列3


个人介绍:

高广超:多年一线互联网研发与架构设计经验,擅长设计与落地高可用、高性能、可扩展的互联网架构。

本文首发在(http://www.jianshu.com/u/2766e4cfc391) 转载请注明!

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

推荐阅读更多精彩内容

  • 我们平时习惯于使用 Rabbitmq 和 Kafka 作为消息队列中间件,来给应用程序之间增加异步消息传递功能。这...
    人生作手阅读 176评论 0 0
  • List 常用命令: Blpop删除,并获得该列表中的第一元素,或阻塞,直到有一个可用 Brpop删除,并获得该列...
    chinariver阅读 986评论 0 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,471评论 28 53
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,133评论 4 8
  • 步骤:发微博01-导航栏内容 -> 发微博02-自定义TextView -> 发微博03-完善TextView和...
    dibadalu阅读 3,095评论 1 3