1. 介绍

redis有一个数据类型叫list(列表),它的每个子元素都是 string 类型的双向链表。我们可以通过 push,pop 操作从链表的头部或者尾部添加删除元素。这使得 list 既可以用作栈,也可以用作队列。

假如,我们有一个队列系统,把一个个任务放到队列中,另一个进程就把队列中的任务取出来执行。

放到队列我们使用LPUSH,也就是往双向链表的尾部填充一个元素,这一端也叫生产者,是产生内容的一端。

另一个进程使用RPOP往头部取出元素来执行,这一端也叫消费者。

如果仅仅是这种方式来实现队列,它就是需要进程不断地循环队列,判断队列是不是有新元素,有的话就取出来执行,没有的话,就继续循环,但是这个总有一个时间间隔,你总得规定每隔一段时间去循环,虽然这个时间很小,但总有延迟,这种方式叫作轮循。有没有一种方式就是让不断执行一个redis命令,而redis中的列队有值就会通过命令通知程序呢?有的,那就是阻塞操作的RPOP,它叫作BRPOP

官方文档有一篇文章An introduction to Redis data types and abstractions是介绍了redis的各种数据结构,其中谈到了list。list部分谈到"Blocking operations on lists",这种就是阻塞版本的list,通常就是用它来实现消息队列的。

2. 实现

我们来演示一下它是如何实现的。

$ redis-cli
127.0.0.1:6379> BRPOP list1 0

先执行BRPOP,假如队列list1没有值,它会返回nil,并且阻塞在那,在等另一个程序或进程往list1中填值。

我们开启另一个redis端终。

$ redis-cli
127.0.0.1:6379> LPUSH list1 a
(integer) 1

我们再来看之前的结果。

127.0.0.1:6379> BRPOP list1 0
1) "list1"
2) "a"
(16.99s)

这样就能把列表的值给取到了。

3. ost

下面我们通过这个叫ost的ruby gem来实现消息队列,并来分析它的源码,来了解redis是如何结合编程语言来实现消息队列的。

先把下面一行添加到Gemfile文件中。

gem 'ost'

接着在config/initializers添加一个文件叫ost.rb,内容如下。

require "ost"

Ost.redis = Redic.new("redis://127.0.0.1:6379")

ost是使用一个轻量级的ruby客户端redic来连接redis的。

消息队列的模型生成两个部分,分别是生产者和消费者,生产者部分就是把访问的文章放到队列中,那就把文章的唯一标识id放到队列就好了。

class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
Ost[:article] << @article.id
end
end

Ost[:article] << @article.id这一部分就相当于上文提到的LPUSH

现在可以打开redis-cli,运行监控命令来查看redis中的状态。

$ redis-cli
127.0.0.1:6379> monitor
OK

我们在页面上随便刷新一篇文章,然后可以在monitor中看到类似下面的信息。

1446890795.971666 [0 127.0.0.1:53622] "LPUSH" "ost:article" "21"

现在生产者好了,要来处理消费者部分。

一般来说我们是要开启另一个进程,但现在我们的重点不在这,我们就用rails console来摸似就好了。

console中运行下面的命令。

Ost[:article].each do |article_id|
@article = Article.find(article_id)
@article.visit_count += 1
@article.save!(validate: false)
end

现在到页面上刷新,再观察visit_count的变化,会发现文章的visit_count会加1的。

而且在monitor中出不断地出现下面的字样。

1446891057.725337 [0 127.0.0.1:54316] "BRPOPLPUSH" "ost:article" "ost:article:MacintoshdeMacBook-Air.local:4188" "2"
1446891059.807253 [0 127.0.0.1:54316] "BRPOPLPUSH" "ost:article" "ost:article:MacintoshdeMacBook-Air.local:4188" "2"
1446891061.827532 [0 127.0.0.1:54316] "BRPOPLPUSH" "ost:article" "ost:article:MacintoshdeMacBook-Air.local:4188" "2"
1446891063.881999 [0 127.0.0.1:54316] "BRPOPLPUSH" "ost:article" "ost:article:MacintoshdeMacBook-Air.local:4188" "2"
1446891065.897304 [0 127.0.0.1:54316] "BRPOPLPUSH" "ost:article" "ost:article:MacintoshdeMacBook-Air.local:4188" "2"

console中的执令就是调用redis的阻塞式的RPOP

现在整个流程已经完成了,ost是怎么实现的呢,这就需要来分析它的源码。

4. ost源码分析

ost这个gem只有一个源文件,总共有77行代码。

其中最主要的有下面的部分。

def push(value)
redis.call("LPUSH", @key, value)
end def each(&block)
loop do
item = redis.call("BRPOPLPUSH", @key, @backup, TIMEOUT) if item
block.call(item)
redis.call("LPOP", @backup)
end break if @stopping
end
end alias << push
alias pop each

Ost[:article] << @article.id就对应上面的push方法,而Ost[:article].each部分就对应each方法。

each方法的代码中可以看到调用了loop循环BRPOPLPUSHBRPOPLPUSH是另一个阻塞版本的RPOP,它可以接超时的时间。

完结。

redis实现消息队列(七)的更多相关文章

  1. Redis 做消息队列

    一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现.定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...

  2. Redis作为消息队列服务场景应用案例

    NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例   一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更 ...

  3. redis resque消息队列

    Resque 目前正在学习使用resque .resque-scheduler来发布异步任务和定时任务,为了方便以后查阅,所以记录一下. resque和resque-scheduler其优点在于功能比 ...

  4. 【springboot】【redis】springboot+redis实现发布订阅功能,实现redis的消息队列的功能

    springboot+redis实现发布订阅功能,实现redis的消息队列的功能 参考:https://www.cnblogs.com/cx987514451/p/9529611.html 思考一个问 ...

  5. 【Redis】php+redis实现消息队列

    在项目中使用消息队列一般是有如下几个原因: 把瞬间服务器的请求处理换成异步处理,缓解服务器的压力 实现数据顺序排列获取 redis实现消息队列步骤如下: 1).redis函数rpush,lpop 2) ...

  6. Lumen开发:结合Redis实现消息队列(1)

    1.简介 Lumen队列服务为各种不同的后台队列提供了统一的API.队列允许你推迟耗时任务(例如发送邮件)的执行,从而大幅提高web请求速度. 1.1 配置 .env文件的QUEUE_DRIVER选项 ...

  7. Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流

    1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...

  8. sping+redis实现消息队列的乱码问题

    使用spring支持redis实现消息队列,参考官方样例:https://spring.io/guides/gs/messaging-redis/ 实现后在运行过程中发现消费者在接收消息时会出现乱码的 ...

  9. 程序员过关斩将--redis做消息队列,香吗?

    Redis消息队列 在程序员这个圈子打拼了太多年,见过太多的程序员使用redis,其中一部分喜欢把redis做缓存(cache)使用,其中最典型的当属存储用户session,除此之外,把redis作为 ...

随机推荐

  1. OSG描边特效osgFX::Outline的修改

    对一个三维场景中的物体实现描边特效,可以参考osg范例osgoutline 这个描边特效使用了模板缓存Stencil来实现,参见源代码osgFX/Outline.cpp 使用了两个Pass 第一个Pa ...

  2. Kafka 2.0 ConsumerGroupCommand新功能

    一直觉得kafka-consumer-groups.sh的输出信息有点少,总算在2.0中得到了改善.新版本ConsumerGroupCommand增加了查看成员信息.组状态信息,算是弥补了之前的不足. ...

  3. Jenkins和Sonar集成

    Jenkins可以通过插件的形式和Sonar很好的集成. (1)Jenkin安装Sonar插件(这里我估计安装的插件有点多) 注意:之前安装Jenkins的时候我用的是JDK系统环境环境变量jdk1. ...

  4. rest_framework框架

    rest_framework框架的认识 它是基于Django的,帮助我们快速开发符合RESTful规范的接口框架. 一  路由 可以通过路由as_view()传参 根据请求方式的不同执行对应不同的方法 ...

  5. day_5.28 py网络编程

    端口 socket简介: socket为一个类   s接收的是返回的对象引用 2018-5-28 15:52:47 开始进行网络编程 udp 套接字 encode() 编码 decode() 解码 ' ...

  6. 解决Hibernate:could not initialize proxy - no Session(申明:来源于网络)

    解决Hibernate:could not initialize proxy - no Session 地址:http://blog.csdn.net/chenssy/article/details/ ...

  7. 数据库系统Informix为例,介绍改善用户查询计划的方法。

    数据库系统Informix为例,介绍改善用户查询计划的方法. 1.合理使用索引 索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率.现在大多数的数据库产品都采用IBM最先提出的ISAM索引 ...

  8. ipv6禁用导致rpcbind服务启动失败解决办法

    参考文档:http://blog.51cto.com/hld1992/2055028

  9. [No0000CB]如何在命令行(cmd)通过TCP/IP端口(port)查询所在的进程号(pid)或进程名称,并终止该进程

      1)首先查找占用某个端口的进程PID netstat -ano | findstr [port] 2)根据该进程pid查询进程名称或标题,确认那个程序在占用该端口 tasklist /v | fi ...

  10. css中display:inline-block display:-moz-inline-box display:-moz-inline-stack 的区别

    很多时候我们必须使一些块元素并排显示,一般想到的是必须使用浮动,但是块元素浮动给边距(margin)的时候在IE下会出现加倍的BUG,所以很多时候不得不把这个块元素套在一个内联元素里面,然后给内联元素 ...