redis实现消息队列(七)
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循环BRPOPLPUSH
,BRPOPLPUSH
是另一个阻塞版本的RPOP
,它可以接超时的时间。
完结。
redis实现消息队列(七)的更多相关文章
- Redis 做消息队列
一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式.利用redis这两种场景的消息队列都能够实现.定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列, ...
- Redis作为消息队列服务场景应用案例
NoSQL初探之人人都爱Redis:(3)使用Redis作为消息队列服务场景应用案例 一.消息队列场景简介 “消息”是在两台计算机间传送的数据单位.消息可以非常简单,例如只包含文本字符串:也可以更 ...
- redis resque消息队列
Resque 目前正在学习使用resque .resque-scheduler来发布异步任务和定时任务,为了方便以后查阅,所以记录一下. resque和resque-scheduler其优点在于功能比 ...
- 【springboot】【redis】springboot+redis实现发布订阅功能,实现redis的消息队列的功能
springboot+redis实现发布订阅功能,实现redis的消息队列的功能 参考:https://www.cnblogs.com/cx987514451/p/9529611.html 思考一个问 ...
- 【Redis】php+redis实现消息队列
在项目中使用消息队列一般是有如下几个原因: 把瞬间服务器的请求处理换成异步处理,缓解服务器的压力 实现数据顺序排列获取 redis实现消息队列步骤如下: 1).redis函数rpush,lpop 2) ...
- Lumen开发:结合Redis实现消息队列(1)
1.简介 Lumen队列服务为各种不同的后台队列提供了统一的API.队列允许你推迟耗时任务(例如发送邮件)的执行,从而大幅提高web请求速度. 1.1 配置 .env文件的QUEUE_DRIVER选项 ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- sping+redis实现消息队列的乱码问题
使用spring支持redis实现消息队列,参考官方样例:https://spring.io/guides/gs/messaging-redis/ 实现后在运行过程中发现消费者在接收消息时会出现乱码的 ...
- 程序员过关斩将--redis做消息队列,香吗?
Redis消息队列 在程序员这个圈子打拼了太多年,见过太多的程序员使用redis,其中一部分喜欢把redis做缓存(cache)使用,其中最典型的当属存储用户session,除此之外,把redis作为 ...
随机推荐
- SpringBoot------MockMvc单元测试
1.添加测试依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www. ...
- NUC972----最简单的驱动(转)
1.新建文本文档,重命名为 hello_dev.c (驱动的开发同应用的开发一样,也是在文本文档下开发的). 2.包含头文件 内核模块需要包含内核相关头文件,不同模块根据功能的差异,所需要的头文件也不 ...
- 【消灭代办】第2周 - 数组判断、开发工具、transform:matrix、Grid
2018.11.19代办一:[数组判断] 代办描述: 怎么判断一个数组是数组呢?其实这个也是一个常考的题目 关键考点: 1.js中对象类型判断的几种方法 2.数组的知识和灵活运用 解决方案s: 篇幅过 ...
- Nginx安装及配置免费HTTPS证书
第一步:安装Nginx 安装Nginx 第二步:安装HTTPS证书( Let's Encrypt) 安装HTTPS证书 第三步骤:浏览器验证 Chrome浏览器打开开发者工具->Security ...
- 计算字符串相似度算法—Levenshtein
什么是Levenshtein Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删 ...
- Android必学-异步加载+Android自定义View源码【申明:来源于网络】
Android必学-异步加载+Android自定义View源码[申明:来源于网络] 异步加载地址:http://download.csdn.net/detail/u013792369/8867609 ...
- SQL中的Continue和Break
x 在Sql Server中,sql语句包含While循环的时候,肯定都或多或少的用到Continue和Break... 下面撸了一个小Demo , Begin ),@Index) Begin Pri ...
- Codeforces 180C - Letter - [简单DP]
题目链接:http://codeforces.com/problemset/problem/180/C 题意: 有一段字符串,包含大小写字母,每次可以将其中一个字母由大写变成小写,或者小写变成大写.要 ...
- JavaScript:改变 HTML 图像
JavaScript:改变 HTML 图像 1.代码如下: <!DOCTYPE HTML> <html> <head> <meta charset=" ...
- 静态方法中只允许访问静态数据,那么,如何在静态方法中访问类的实例成员(即没有附加static关键字的字段或方法)?
package test.two; public class jingtaihanshu { int x = 3; static int y = 4; public static void Meth ...