Laravel中为什么不使用blpop取队列详析


Posted in PHP onAugust 01, 2018

前言

Redis 的 list 数据结构常用来做消息队列,通常使用的命令有 lpop/rpop ,还有带阻塞版的 blpop/brpop 等。Laravel 5.3 消息队列也是用的 lpop 取消息,为什么不用阻塞版的 blpop 呢?

blpop 不用一直轮询,还可以同时取多个队列,blpop high low 30,更方便实现队列的优先级。

安全队列和不安全队列

什么是不安全的队列?比如客户端 lpop(统一以 lpop 为例) 从 redis 取出来的 job(任务)还没处理完进程挂掉了或者遇到了异常,由于此时服务器上已经没有副本了,这个 job 就丢失了。这种队列就是不安全的。

Laravel 正是为了保证消息队列的可靠,进程挂掉了或者处理失败还可以重试等,做了比较完善的机制,如取队列的同时把队列放入另一个集合中“暂存”起来。如代码所示,使用 lpop 取出队列,同时 zadd 到另一个集合,使用 redis lua 来保证原子性。

public static function pop()
{
 return <<<'LUA'
-- Pop the first job off of the queue...
local job = redis.call('lpop', KEYS[1])
local reserved = false
 
if(job ~= false) then
-- Increment the attempt count and place job on the reserved queue...
reserved = cjson.decode(job)
reserved['attempts'] = reserved['attempts'] + 1
reserved = cjson.encode(reserved)
redis.call('zadd', KEYS[2], ARGV[1], reserved)
end
 
return {job, reserved}
LUA;
}

具体 Laravel 队列工作原理之前有一篇博文进行了整理,请参考:https://3water.com/article/131414.htm

为什么不用 blpop?

这里为什么不使用阻塞版本的 blpop 呢?

blpop 是阻塞版的 lpop,如果队列没有数据过来,那么在超时时间内就会一直阻塞,直到 rpush 数据到队列,有点类似 http 的长轮询,假如客户端取出数据的这一刻挂了,还没来得及暂存到另外的集合中,那么这个数据就丢失了。

你可能会问为何不跟 lpop 一样用 lua 脚本来处理并保证原子性?这个问题作者在 github 上有回答。(https://github.com/laravel/framework/issues/22939)

Laravel中为什么不使用blpop取队列详析

我们知道 redis lua 脚本实际上就是事务,作者的大意也是说 MULTI/EXEC 包裹起来的 blpop 没有意义,这个时候它“退化”为非阻塞版的。

Redis 官方文档也有说明:

在MULTI/EXEC事务中的BLPOP

BLPOP 可以用于流水线(pipline,批量地发送多个命令并读入多个回复),但把它用在 MULTI / EXEC 块当中没有意义。因为这要求整个服务器被阻塞以保证块执行时的原子性,该行为阻止了其他客户端执行 LPUSH 或 RPUSH 命令。

因此,一个被包裹在 MULTI / EXEC 块内的 BLPOP 命令,行为表现得就像 LPOP 一样,对空列表返回 nil ,对非空列表弹出列表元素,不进行任何阻塞操作。

因此通过 lua 脚本操作 blpop 和 zadd 也没有意义,结论就是:因为没用到阻塞的特性,或者无法保证原子性。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
PHP动态图像的创建
Oct 09 PHP
《APMServ 5.1.2》使用图解
Oct 23 PHP
一篇不错的PHP基础学习笔记
Mar 18 PHP
php数组函数序列之array_splice() - 在数组任意位置插入元素
Nov 07 PHP
php使用curl模拟登录后采集页面的例子
Nov 04 PHP
Linux编译升级php的详细方法
Nov 04 PHP
一个基于phpQuery的php通用采集类分享
Apr 09 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(一)
Jun 23 PHP
Yii2使用swiftmailer发送邮件的方法
May 03 PHP
如何利用预加载优化Laravel Model查询详解
Aug 11 PHP
PHP中引用类型和值类型功能与用法示例
Feb 26 PHP
redis+php实现微博(一)注册与登录功能详解
Sep 23 PHP
Laravel5.5以下版本中如何自定义日志行为详解
Aug 01 #PHP
PHP实现随机数字、字母的验证码功能
Aug 01 #PHP
PHP使用XMLWriter读写xml文件操作详解
Jul 31 #PHP
laravel + vue实现的数据统计绘图(今天、7天、30天数据)
Jul 31 #PHP
PHP常用日期加减计算方法实例小结
Jul 31 #PHP
ThinkPHP5.0多个文件上传后找不到临时文件的修改方法
Jul 30 #PHP
PHP笛卡尔积实现算法示例
Jul 30 #PHP
You might like
开发大型 PHP 项目的方法
2007/01/02 PHP
php 在线打包_支持子目录
2008/06/28 PHP
PHP版本升级到7.x后wordpress的一些修改及wordpress技巧
2015/12/25 PHP
PHP使用php-resque库配合Redis实现MQ消息队列的教程
2016/06/29 PHP
Laravel构建即时应用的一种实现方法详解
2017/08/31 PHP
Django 标签筛选的实现代码(一对多、多对多)
2018/09/05 PHP
Yii框架学习笔记之应用组件操作示例
2019/11/13 PHP
js实现的网站首页随机公告随机公告
2007/03/14 Javascript
利用js跨页面保存变量做菜单的方法
2008/01/17 Javascript
JS操作iframe里的dom(实例讲解)
2014/01/29 Javascript
使用JS或jQuery模拟鼠标点击a标签事件代码
2014/03/10 Javascript
js动态创建及移除div的方法
2015/06/03 Javascript
JavaScript基于ajax编辑信息用法实例
2015/07/15 Javascript
轻松学习jQuery插件EasyUI EasyUI创建树形菜单
2015/11/30 Javascript
js面向对象的写法
2016/02/19 Javascript
hovertree插件实现二级树形菜单(简单实用)
2016/12/28 Javascript
Angularjs使用过滤器完成排序功能
2017/09/20 Javascript
使用Layer组件弹出多个对话框(非嵌套)与关闭及刷新的例子
2019/09/25 Javascript
基于js判断浏览器是否支持webGL
2020/04/18 Javascript
js获取图片的base64编码并压缩
2020/12/05 Javascript
Python编程之string相关操作实例详解
2017/07/22 Python
python把数组中的数字每行打印3个并保存在文档中的方法
2018/07/17 Python
用Python识别人脸,人种等各种信息
2019/07/15 Python
Python实现大数据收集至excel的思路详解
2020/01/03 Python
Python OpenCV读取显示视频的方法示例
2020/02/20 Python
Pytorch高阶OP操作where,gather原理
2020/04/30 Python
appium+python自动化配置(adk、jdk、node.js)
2020/11/17 Python
Python爬虫之Selenium库的使用方法
2021/01/03 Python
HTML5 本地存储之如果没有数据库究竟会怎样
2013/04/25 HTML / CSS
加拿大廉价机票预订网站:CheapOair.ca
2018/03/04 全球购物
英国美术用品购物网站:Cass Art
2019/10/08 全球购物
公司周年庆典邀请函
2014/01/12 职场文书
卖车协议书范例
2014/09/16 职场文书
2015年宣传思想工作总结
2015/05/22 职场文书
2016年小学生新年寄语
2015/08/18 职场文书
2016幼儿园新学期寄语
2015/12/03 职场文书