用redis做简单的任务队列(一)
队列本身其实是个有序的列表,而Redis是支持list的,我们可以查看Redis的官方文档 http://redis.io/commands#list,其中我们可以对这个队列的两端分别进行操作,所以其实Redis中的list即可以当做普通的先进先出的queue,也可以作为先进后出的stack。
如果当做队列来用,我们可以用LPUSH(头部插入)和RPOP(尾部弹出)或RPUSH(尾部插入)和LPOP(头部弹出),这两种方式都可以,只要是搭配使用即可。但我们平时一般搭配使用 LPUSH和RPOP。
一般开发的时候我们会分为生产者和消费者,生产者生产消息,消费者获取消息进行处理。
Producer:
redis->lpush(‘joblist’, ‘this is Job-1’);
redis->lpush(‘joblist’, ‘this is Job-2’);
….
Cosumer:
job = redis->rpop(‘joblist’);// return Job-1
done the job….
job = redis->rpop(‘joblist’);// return Job-2
done the job…
正常情况下上面这些都是没问题的,但是我们也经常遇到这种情况,当任务进行到一半(即任务没有完全执行完)的时候,突然服务器宕机或者网络中断,这时候任务其实是没有真正完成的,这时候出现队列中的任务“丢失”了,并不是说队列任务真的丢失了,而是我们把任务从队列拿出来之后并没有完成这个任务,这时候我们就需要考虑如何能在任务真正完成的时候才把任务从队列中删除。
幸运地是,redis其实给我们提供了这样的可能。继续翻看redis的官方文档,我们发现 RPOPLPUSH,从字面含义来解释就是尾部弹出头部插入,它后面跟两个参数,一个是弹出的list,一个是要插入的list,而且文档上告诉我们它可以实现 可靠的队列。我们可以把任务从队列中取出来放到另外一个执行中的队列,等到任务真正完成之后再从执行中的队列中删除。
那么上面的代码我们可以修改成如下,producer不变:
Cosumer:
job = redis->rpoplpush(‘joblist’, ‘job-doing’);// 把Job-1从joblist转移到job-doing
done the job….// 完成Job-1
redis->lrem(‘job-doing’, 1, job);// 等Job-1完成之后把它从job-doing队列中删除
这样如果Job失败,那Job的任务还存在在队列job-doing中,我们可以单独启一个进程来扫描job-doing列表,如果一个job长时间在队列中,则重新执行该Job或重新插入的job列表。
当然,这也不是完全100%靠谱的解决方案,因为如果存在很多相同的job,那我们从job-doing删除job的时候就无法确认是哪个job真正应该删除,但是像我们上面描述的,既然Job是相同的,那我们删除哪个也无所谓了,反正执行的结果都一样。上面的解决方案还有一个问题,我们必须要长时间地监控job-doing列表,这需要额外的资源,还有一种方式是job-doing和job用同一个列表,即:
Cosumer:
job = redis->rpoplpush(‘joblist’, ‘jobllist’); 把Job-1从joblist尾部转移到joblist头部
done the job….// 完成Job-1
redis->lrem(‘job-doing’, 1, job);// 等Job-1完成之后把它从joblist头部删除
其实,文章写到这里,大家应该明白了,这样一来,我们利用循环队列,即可实现可靠的队列。当然,相比较世面上比较成熟的队列,例如 RabbitMQ、 Beanstalkd、 IronMQ等还是有很多的不足,但是如果是简单地项目或者项目本身就已经在使用redis又不想添加新的组件,试试redis list也不错。
用redis做简单的任务队列(一)的更多相关文章
- 用redis做简单的任务队列(二)
是用redis做任务队列时,要思考: 用什么数据类型来做任务队列 怎样才能防止重复爬取 上一篇文章已经决定使用list来做任务队列,但是去重问题没有得到解决.这里可以用set来解决思考二的问题,就是防 ...
- 程序员修神之路--redis做分布式锁可能不那么简单
菜菜哥,复联四上映了,要不要一起去看看? 又想骗我电影票,对不对? 呵呵,想去看了叫我呀 看来你工作不饱和呀 哪有,这两天我刚基于redis写了一个分布式锁,很简单 不管你基于什么做分布式锁,你觉得很 ...
- 使用Redis做MyBatis的二级缓存
使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...
- redis 的简单命令
以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...
- spring-boot集成mybatis,用redis做缓存
网上有很多例子了,执行源码起码有3个,都是各种各样的小问题. 现在做了个小demo,实现spring-boot 用redis做缓存的实例,简单记录下思路,分享下源码. 缓存的实现,分担了数据库的压力, ...
- 使用Redis做分布式
一 为什么使用 Redis 在项目中使用 Redis,主要考虑两个角度:性能和并发.如果只是为了分布式锁这些其他功能,还有其他中间件 Zookpeer 等代替,并非一定要使用 Redis. 性能: 如 ...
- NetCore入门篇:(九)Net Core项目使用Session及用Redis做分布式
一.简介 1.因为Net Core默认是没有启动Session功能的,如果需要使用,需要通过代码开启. 2.本篇说明如果启用默认Session实现,即Session存到内存中. 3.本篇扩展说明如何用 ...
- Redis的简单了解以及主从复制
1.Redis的简单了解 Redis是一种高性能的分布式NoSql数据库,持久存储,高并发,数据类型丰富,通过现场申请内存空间,同时可以配置虚拟内存.五种数据类型:string(字符串,这种格式和me ...
- Redis主从复制简单介绍
由于本地环境的使用,所以搭建一个本地的Redis集群,本篇讲解Redis主从复制集群的搭建,使用的平台是Windows,搭建的思路和Linux上基本一致! (精读阅读本篇可能花费您15分钟,略读需5分 ...
随机推荐
- bootstrap5
列表组的使用 ul.list-group > li.list-group-item *5... 列表组中可以放置徽标: 在li中放置 span.badge. bootstrap中的情景类: 实际 ...
- 获取CheckBox的值
前台获取 function chkCheckBox() { var code_arr = new Array(); //定义一数组 $('.C_B').each(function () { if ($ ...
- Cocos2d-x学习笔记(五)调度
在init方法中增加下边的代码,建议使用schedule函数,而不是scheduleUpdate函数,因为,后者默认是调用update函数,在如果有多个函数需要调度时,不是很灵活. auto labe ...
- ubuntu14.04, keyboard shortcuts
- shell 脚本拼接
var21=`echo $vvar|awk -F ',' '{print $1}'` echo $var21 var31=`echo $var21|awk -F ':' '{print $2}'` e ...
- MongoDB(课时18 修改器)
3.4.3.2 修改器(原子操作) 对MongoDB数据库而言,数据的修改会牵扯到内容的变更,结构的变更(包含数组),所以在MongoDB在设计的时候就提供有一系列的修改器的应用,那么像之前使用的“$ ...
- 《F4+2》β冲刺第二天
β冲刺第二天 1.每个成员今日完成的任务: 马仲山:系统代码和开发总结文档的完善 马婧(12):完善需求文档 马婧(13):完善设计文档 马世芳:对部分功能实现进行测试 张俊逸:针对测试出现的问题完善 ...
- 《WAP团队》项目系统设计改进与详细设计
任务1: 一.分析<基于家教管理系统项目需求规格说明书>初稿的不足,特别是文档需求描述建模不完整的内容. 通过软件工程更深入的学习发现我们的需求分析建模明显不足,缺少最重要的类图. 二.团 ...
- jq 命名空间
$('ul').on("click",function(){console.log('all');});$('ul').on("click.a",functio ...
- 雷林鹏分享:C# 反射(Reflection)
C# 反射(Reflection) 反射(Reflection) 对象用于在运行时获取类型信息.该类位于 System.Reflection 命名空间中,可访问一个正在运行的程序的元数据. Syste ...