你知道为什么Laravel会重复执行同一个队列任务吗?
发布时间 - 2021-04-29 00:00:00 点击率:次下面由laravel教程栏目给大家介绍为什么 laravel 会重复执行同一个队列任务,希望对需要的朋友有所帮助!
在 Laravel 中使用 Redis 处理队列任务,框架提供的功能非常强大,但是最近遇到一个问题,就是发现一个任务被多次执行,这是为什么呢?
先说原因:因为在 Laravel 中如果一个队列(任务)执行时间大于 60 秒,就会被认为执行失败并重新加入队列中,这样就会导致重复执行同一个任务。
这个任务的逻辑就是给用户推送内容,需要根据队列内容取出用户并遍历,通过请求后端 HTTP 接口发送。比如有 10000 个用户,在用户数量多或接口处理速度没那么快的情况下,执行时间肯定会大于 60 秒,于是这个任务就被重新加入队列。情况更糟糕一点,前面的任务如果都没有在 60 秒执行完,就都会重新加入队列,这样同一个任务就不止重复执行一次了,而是多次。
下面从 Laravel 源代码找一下罪魁祸首。
源代码文件:vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php
/** * The expiration time of a job. * * @var int|null */ protected $expire = 60;
这个 $expire 成员变量是一个固定的值,Laravel 认为一个队列再怎么 60 秒也该执行完了吧。取队列方法:
public function pop($queue = null)
{
$original = $queue ?: $this->default;
$queue = $this->getQueue($queue);
$this->migrateExpiredJobs($queue.':delayed', $queue);
if (! is_null($this->expire)) {
$this->migrateExpiredJobs($queue.':reserved', $
queue);
}
list($job, $reserved) = $this->getConnection()->eval(
LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire
);
if ($reserved) {
return new RedisJob($this->container, $this, $job, $reserved, $original);
}
}取队列有几步操作,因为队列执行失败,或执行超时等都会放入另外的集合保存起来,以便重试,过程如下:
1.把因执行失败的队列从 delayed 集合重新 rpush 到当前执行的队列中。
2.把因执行超时的队列从 reserved 集合重新 rpush 到当前执行的队列中。
3.然后才是从队列中取任务开始执行,同时把队列放入 reserved 的有序集合。
这里使用了 eval 命令执行这个过程,用到了几个 lua 脚本。
从要执行的队列中取任务:
local job = redis.call('lpop', KEYS[1])
local reserved = false
if(job ~= false) then
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}可以看到 Laravel 在取 Redis 要执行的队列的时候,同时会放一份到一个有序集合中,并使用过期时间戳作为分值。
只有当这个任务完成后,再把有序集合中这个任务移除。从这个有序集合移除队列的代码就省略,我们看一下 Laravel 如何处理执行时间大于 60 秒的队列。
也就是这段 lua 脚本执行的操作:
local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])
if(next(val) ~= nil) then
redis.call('zremrangebyrank', KEYS[1], 0, #val - 1)
for i = 1, #val, 100 do
redis.call('rpush', KEYS[2], unpack(val, i, math.min(i+99, #val)))
end
end
return true这里 zrangebyscore 找出分值从无限小到当前时间戳的元素,也就是 60 秒之前加入到集合的任务,然后通过 zremrangebyrank 从集合移除这些元素并 rpush 到队列中。
看到这里应该就恍然大悟了。
如果一个队列 60 秒没执行完,那么进程在取队列的时候从 reserved 集合中把这些任务又重新 rpush 到队列中。
相关推荐:最新的五个Laravel视频教程
# php
# laravel
# redis
# 执行时间
# 就会
# 移除
# 源代码
# 分值
# 中取
# 是一个
# 这是
# 几个
# 如有
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel模型事件有哪些_Laravel Model Event生命周期详解
韩国服务器如何优化跨境访问实现高效连接?
如何在云主机上快速搭建网站?
Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置
详解jQuery停止动画——stop()方法的使用
如何用IIS7快速搭建并优化网站站点?
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】
JavaScript如何实现继承_有哪些常用方法
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
如何在宝塔面板创建新站点?
高端建站如何打造兼具美学与转化的品牌官网?
软银砸40亿美元收购DigitalBridge 强化AI资料中心布局
javascript读取文本节点方法小结
高端网站建设与定制开发一站式解决方案 中企动力
如何用AWS免费套餐快速搭建高效网站?
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
东莞市网站制作公司有哪些,东莞找工作用什么网站好?
EditPlus中的正则表达式实战(6)
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?
php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】
Laravel DB事务怎么使用_Laravel数据库事务回滚操作
高防网站服务器:DDoS防御与BGP线路的AI智能防护方案
JavaScript如何操作视频_媒体API怎么控制播放
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
Laravel如何保护应用免受CSRF攻击?(原理和示例)
Laravel如何构建RESTful API_Laravel标准化API接口开发指南
香港服务器租用费用高吗?如何避免常见误区?
详解MySQL数据库的安装与密码配置
Laravel distinct去重查询_Laravel Eloquent去重方法
Laravel如何配置和使用缓存?(Redis代码示例)
如何实现建站之星域名转发设置?
WordPress 子目录安装中正确处理脚本路径的完整指南
如何用搬瓦工VPS快速搭建个人网站?
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
实例解析Array和String方法
C#如何调用原生C++ COM对象详解
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
如何在阿里云完成域名注册与建站?
Laravel如何自定义分页视图?(Pagination示例)
微信小程序 配置文件详细介绍
Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案
Laravel如何使用.env文件管理环境变量?(最佳实践)
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
Laravel怎么防止CSRF攻击_Laravel CSRF保护中间件原理与实践
如何快速使用云服务器搭建个人网站?
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
做企业网站制作流程,企业网站制作基本流程有哪些?


queue);
}
list($job, $reserved) = $this->getConnection()->eval(
LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire
);
if ($reserved) {
return new RedisJob($this->container, $this, $job, $reserved, $original);
}
}