很久没有写博客了,最近简单的学习了一下Redis,其中学习了一下用Redis实现优先级消息队列。关于更多更为详细的可以在www.redis.cn找到相关资料。
对于熟悉Redis的童鞋提到队列很自然的想到使用Redis的列表类型。其中也自然能够想到LPUSH和RPOP命令实现队列的概念。如果要实现任务队列,只需要让生产者将任务使用LPUSH命令加入到某个键中,另一边让消费者不断使用RPOP命令从该键中取出任务即可。首先我们先进行简单测试,分别打开两个redis-cli实例
在第一个窗口中输入:
127.0.0.1:6379> LPUSH queue:0 yayun (integer) 1 127.0.0.1:6379>
第二个窗口输入:
127.0.0.1:6379> RPOP queue:0 "yayun" 127.0.0.1:6379> LLEN queue:0 (integer) 0 127.0.0.1:6379>
可以看见马上就取出值了,再次查询的时候发现已经没有记录了,说明已经消费掉了。简单明了的生产者,消费者。
如果我们程序中使用RPOP命令,将导致队列中即使没有任务时,也会每秒都会调用RPOP命令,如果可以实现一旦有新任务加入就通知消费者,也就是我们的程序,那么将是最给力的,Redis都已经替我们想好了,我们可以使用BRPOP命令操作,BRPOP命令和RPOP命令相似,唯一的区别是当列表中没有元素时BRPOP命令会一直阻塞住连接,直到有新的任务加入。我们再次进行简单测试。
BRPOP命令接收两个参数,第一个是键名,第二个是超时时间,单位是秒。当超过了此时间没有获得性元素的话返回nil。如果设置为0,表示不限制等待时间,即如果没有新元素加入列表就会永远阻塞下去。
第一个窗口中输入(没有任务一直阻塞):
127.0.0.1:6379> BRPOP queue:0 0
第二个窗口中输入:
127.0.0.1:6379> LPUSH queue:0 yayun (integer) 1 127.0.0.1:6379>
当我们加入任务时,发现第一个窗口的输入如下:
127.0.0.1:6379> BRPOP queue:0 0 1) "queue:0" 2) "yayun" (66.19s) 127.0.0.1:6379>
可以发现当加入任务时马上消费掉了,元素已经被取走。
下面说说如何实现优先级消息队列,最后结合python来一段简单代码演示一下。BRPOP命令可以同时接收多个键,起完整的命令格式为BRPOP key [ key ... ] timeout,例如BRPOP queue:0 queue:1 0。意思是同时检测多个键,如果所有键都没有元素则一直阻塞,如果其中有一个键有元素会从该键中弹出元素。如果多个键都有元素则按照从左到右的顺序取第一个键中的一个元素。下面进行简单的测试。打开两个实例:
第一个窗口中:
127.0.0.1:6379> LPUSH queue:0 yayun (integer) 1 127.0.0.1:6379> LPUSH queue:1 dengyayun (integer) 1 127.0.0.1:6379>
第二个窗口中:
127.0.0.1:6379> LPUSH queue:0 yayun (integer) 1 127.0.0.1:6379> BRPOP queue:0 queue:1 0 1) "queue:0" 2) "yayun" 127.0.0.1:6379> BRPOP queue:0 queue:1 0 1) "queue:1" 2) "dengyayun" 127.0.0.1:6379>
从上面我们可以发现queue:0任务先执行了。好了,说了这么多了相信大家都测试体会到了,下面来一段简单的python脚本进行测试。python操作redis需要安装模块。
从https://github.com/andymccurdy/redis-py下载redis-py-master.zip安装,解压后进入到目录执行python setup.py install即可安装完成。关于相关操作说明这里说的非常清楚https://pypi.python.org/pypi/redis/以及解压后目录里的README.rst文件。都可以进行阅读。
生产着脚本如下:
#!/usr/bin/python import redis def producers(): for i in xrange(1000000): str='low_task_queue %d' % i pool = redis.ConnectionPool(host='localhost', port=6379, db=0) r = redis.Redis(connection_pool=pool) r.lpush('low_task_queue',str) if __name__ == "__main__": producers()
消费者脚本如下:
#!/usr/bin/python import redis, time def handle(task): print task time.sleep(1) def consumer(): pool = redis.ConnectionPool(host='localhost', port=6379, db=0) r = redis.Redis(connection_pool=pool) while 1: result = r.brpop(['high_task_queue', 'low_task_queue'], 0) handle(result[1]) if __name__ == "__main__": consumer()
打开2个窗口,分别执行两个脚本,然后打开第3个窗口手动添加元素,模拟优先级队列。
可以看见在处理队列了,我这里停顿了1秒才处理,是为了观看效果。
[root@mysql-server-03 ~]# python consumer.py low_task_queue 0 low_task_queue 1 low_task_queue 2 low_task_queue 3 low_task_queue 4 low_task_queue 5 low_task_queue 6 low_task_queue 7 low_task_queue 8 low_task_queue 9 low_task_queue 10 low_task_queue 11 low_task_queue 12 low_task_queue 13 low_task_queue 14
打开窗口手动插入元素:
127.0.0.1:6379> LPUSH high_task_queue 'high_task_queue 9999999' (integer) 1 127.0.0.1:6379>
可以看见优先级队列已经处理。
low_task_queue 213 low_task_queue 214 low_task_queue 215 low_task_queue 216 low_task_queue 217 low_task_queue 218 low_task_queue 219 low_task_queue 220 low_task_queue 221 low_task_queue 222 low_task_queue 223 low_task_queue 224 high_task_queue 9999999 low_task_queue 225 low_task_queue 226 low_task_queue 227 low_task_queue 228
参考资料
<<Redis 入门指南>>
总结:
Redis功能强大,简单易用,还需要进一步好好学习。