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. Java虚拟机(二):Java GC算法 垃圾收集器

    概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本地方 ...

  2. MySQL临时表创建及旧表建新表

    1.创建临时表 临时表是一张表,用来临时保存一些数据 特点: 只对创建该临时表的用户可见: 当会话结束时,MySQL自动删除临时表. 临时表的核心:建表和删表消耗资源极其少 创建临时表的基本格式: C ...

  3. [Android] 基于 Linux 命令行构建 Android 应用(一):关于 Android 项目

    关于 Android 项目 项目是保存源代码和资源文件的容器. 谷歌提供的 Android SDK 工具只能对具有固定目录结构的项目进行编译和打包.因此强烈建议使用 Eclipse + ADT 或者 ...

  4. Erlang的crypto模块与最新的openssl动态链接库不兼容的问题与解决方案

    在2014新年伊始,增买了一台阿里云服务器,装的系统是CentOS 6.3 64位,装完Erlang后,出现了下面的情况: ./configure --without-javac --with-ssl ...

  5. 【本周主题】第三期 - JavaScript 内存机制

  6. 【react】---styled-components的基本使用---【巷子】

    一.官网地址 https://www.styled-components.com/ 二.styled-components 1.styled-components 样式化组件,主要作用是它可以编写实际 ...

  7. [No000018F]Vim自动缩进配置、原理和tab键替换空格-Vim使用技巧(4)

    一.Vim缩进介绍 在没有设置Vim自动缩进的条件下,可以手动使用Vim命令对特定行进行缩进处理.在Vim插入模式下,按下 Tab 键时默认会输入一个制表符,可通过Vim配置项将 Tab 替换为空格, ...

  8. MySQL复制原理-加强版

    mysql从3.23开始提供复制功能,复制指将主库的ddl和dml操作通过binlog文件传送到从库上执行,从而保持主库和从库数据同步.mysql支持一台主库同时向多台从库复制,从库同时也可以作为其他 ...

  9. 会话(Session)与cookies

    由于http是无状态的,向服务器发送请求后,服务器解析请求然后返回对应的响应,服务器负责完成这个过程是完全独立的,不会记录前后状态的变化,因此缺少状态记录. 我们分别需要会话和Cookies的技术来保 ...

  10. Laravel 5.2 INSTALL- node's npm and ruby's bundler.

    https://getcomposer.org/doc/00-intro.md Introduction# Composer is a tool for dependency management i ...