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 相关文章推荐
第十节 抽象方法和抽象类 [10]
Oct 09 PHP
PHP判断远程图片或文件是否存在的实现代码
Feb 20 PHP
php导出csv格式数据并将数字转换成文本的思路以及代码分享
Jun 05 PHP
PHP中ini_set与ini_get用法实例
Nov 04 PHP
PHP中mysql_field_type()函数用法
Nov 24 PHP
CentOS下PHP7的编译安装及MySQL的支持和一些常见问题的解决办法
Dec 17 PHP
php使用FFmpeg接口获取视频的播放时长、码率、缩略图以及创建时间
Nov 07 PHP
CakePHP框架Session设置方法分析
Feb 23 PHP
thinkPHP5.0框架整体架构总览【应用,模块,MVC,驱动,行为,命名空间等】
Mar 25 PHP
laravel 模型查询按照whereIn排序的示例
Oct 16 PHP
PHP实现文件上传后台处理脚本
Mar 04 PHP
php实现微信和支付宝支付的示例代码
Aug 11 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做的端口嗅探器--可以指定网站和端口
2006/10/09 PHP
php过滤危险html代码
2008/08/18 PHP
PHP调用C#开发的dll类库方法
2014/07/28 PHP
PHP copy函数使用案例代码解析
2020/09/01 PHP
JavaScript基本概念初级讲解论坛贴的学习记录
2009/02/22 Javascript
利用jQuery 实现GridView异步排序、分页的代码
2010/02/06 Javascript
javascript改变position值实现菜单滚动至顶部后固定
2013/01/18 Javascript
jQuery照片伸缩效果不影响其他元素的布局
2014/05/09 Javascript
jQuery学习笔记之jQuery中的$
2015/01/19 Javascript
JavaScript实现Iterator模式实例分析
2015/06/09 Javascript
JavaScript 七大技巧(一)
2015/12/13 Javascript
JavaScript与jQuery实现的闪烁输入效果
2016/02/18 Javascript
jQuery+css实现非常漂亮的水平导航菜单效果
2016/07/27 Javascript
JavaScript中绑定事件的三种方式及去除绑定
2016/11/05 Javascript
Angular路由简单学习
2016/12/26 Javascript
seajs下require书写约定实例分析
2018/05/16 Javascript
Vue Router的懒加载路径的解决方法
2018/06/21 Javascript
js字符串处理之绝妙的代码
2019/04/05 Javascript
vuex(vue状态管理)的特殊应用案例分享
2020/03/03 Javascript
Vue实现导航栏菜单
2020/08/19 Javascript
[57:59]完美世界DOTA2联赛循环赛 Ink Ice vs LBZS BO2第一场 11.05
2020/11/05 DOTA
[01:33]PWL开团时刻DAY2-开雾与反开雾
2020/10/31 DOTA
[01:06:19]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第二场 1月8日
2021/03/11 DOTA
python根据出生日期获得年龄的方法
2015/03/31 Python
使用实现pandas读取csv文件指定的前几行
2018/04/20 Python
Django之模型层多表操作的实现
2019/01/08 Python
pycharm修改界面主题颜色的方法
2019/01/17 Python
Python中psutil的介绍与用法
2019/05/02 Python
详解Python利用configparser对配置文件进行读写操作
2020/11/03 Python
详解BeautifulSoup获取特定标签下内容的方法
2020/12/07 Python
Python字符串对齐、删除字符串不需要的内容以及格式化打印字符
2021/01/23 Python
Biblibili视频投稿接口分析并以Python实现自动投稿功能
2021/02/05 Python
保密工作实施方案
2014/02/24 职场文书
电子信息工程自荐信
2014/05/26 职场文书
农村文化活动总结
2014/08/28 职场文书
高质量“欢迎词”
2019/04/03 职场文书