源码分析 Laravel 重复执行同一个队列任务的原因

news2025/10/28 1:34:23

发现问题

在 Laravel 中使用 Redis 处理队列任务,框架提供的功能非常强大,但是最近遇到一个问题,就是发现一个任务被多次执行,这是为什么呢?

先说原因:

因为在 Laravel 中如果一个队列(任务)执行时间大于 60 秒,就会被认为执行失败并重新加入队列中,这样就会导致重复执行同一个任务。

这个任务的逻辑就是给用户推送内容,需要根据队列内容取出用户并遍历,通过请求后端 HTTP 接口发送。比如有 10000 个用户,在用户数量多或接口处理速度没那么快的情况下,执行时间肯定会大于 60 秒,于是这个任务就被重新加入队列。情况更糟糕一点,前面的任务如果都没有在 60 秒执行完,就都会重新加入队列,这样同一个任务就不止重复执行一次了,而是多次。

下面从 Laravel 源代码找一下罪魁祸首。

源代码文件:vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php

1

2

3

4

5

6

/**

 * The expiration time of a job.

 *

 * @var int|null

 */

protected $expire = 60;

这个 $expire 成员变量是一个固定的值,Laravel 认为一个队列再怎么 60 秒也该执行完了吧。取队列方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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 脚本。

从要执行的队列中取任务:

1

2

3

4

5

6

7

8

9

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 脚本执行的操作:

1

2

3

4

5

6

7

8

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 到队列中。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/33761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java项目:JSP民宿预订网站信息管理平台

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为前后台,包括管理员与普通用户两种角色; 管理员角色包含以下功能: 管理员登录,用户管理,会员管理,…

高斯消元总结

A-Matrix Equation_第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)(重现赛) 自己写一个2维矩阵或者3维矩阵就可以发现对于每一列来说都是独立的,每一列的n个Cij都是都关系的&#xff0c…

Sentinel熔断降级

Sentinel熔断降级 1、介绍 官网地址:https://github.com/alibaba/Sentinel/wiki/熔断降级 Sentinel 提供以下几种熔断策略: 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最…

cesium for ue5中actor的cesium georeference组件的改变

今天,重新回顾下cesium for ue的教程,actor添加cesium georeference组件 发现在ue5中,没有这个组件了, 忍住卸载ue5,重新安装ue4.27的冲动,cesium的组件数量增多了,反而功能减少了&#xff…

hive的安装配置及使用

hive需要MySQL和hadoop的支持 文章目录hive需要MySQL和hadoop的支持安装完毕MySQL之后下载并解压hive添加系统环境变量配置hive修改 vim hive-env.sh修改hive-site.xml(1、简化配置(推荐))修改hive-site.xml(2、在简化的基础上添加了部分配置)下载一个my…

Java项目:JSP药店药品商城管理系统

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台,分为管理员与普通用户两种角色,管理员登录后台,普通用户登录前台; 管理员角色…

VMware使用教程

文章目录下载软件安装软件新建虚拟机自定义安装下载软件 下载VMware软件(应该是需要许可证/密钥的,时间长有点忘了,当然建议买,拒绝白嫖,保护产权(可以直接百度搜doge))。 安装软件…

Sentinel流控规则

Sentinel流控规则 1、基本介绍 资源名:唯一名称,默认请求路径(如:http://localhost:8089/testA) 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认defa…

Bioinformatics2021 | FraGAT+:基于分子原始片段的多尺度图注意力机制的分子性质预测模型

原文标题:FraGAT: a fragment-oriented multi-scale graph attention model for molecular property prediction 论文地址:FraGAT: a fragment-oriented multi-scale graph attention model for molecular property prediction | Bioinformatics | Oxfo…

Windows OpenGL ES 图像阴影

目录 一.OpenGL ES 图像阴影 1.原始图片2.效果演示 二.OpenGL ES 图像阴影源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 OpenGL E…

SuperMap 云原生动态伸缩不可用该如何解决

最近碰到个别用户在ARM环境国产化操作系统中使用SuperMap iManager for K8S的时候出现服务节点伸缩不生效的问题。今天咱们就以此例来给大家讲解下如何解决该问题。 问题现象:在云套件服务节点管理中,设置伸缩为动态伸缩,设置阈值条件后&…

web前端-javascript-自增++和自减--(a++和++a,都在自身基础上+1,a--和--a都在自增基础上-1,自增和自减的练习)

文章目录自增和自减--1. 自增 1.1 通过自增可以使变量在自身的基础上增加1.2 对一个变量自增以后,原变量的值会立即自增 11.3 自增分为两种,:后(a) 和 前(a)2. 自减 --2.1 通过自减可以使变量在自身的基础上减 12.2 自减分成两种,…

(热门)智慧社区助力实现社区数字化转型

社区是人们生活的重要场所,传统社区的运营及管理方式已经无法满足人们日益增长的物质和文化生活需要。社区的通讯、安全和社区居民信息的收集、处理及共享等问题都成为阻碍社区服务进一步发展的瓶颈。 智慧社区是利用物联网、云计算、移动互联网、信息智能终端等新…

前后端分离项目,vue+uni-app+php+mysql订座预约系统设计与实现(H5移动项目)

功能介绍 【后台功能】 广告管理:设置小程序首页轮播图广告和链接 留言列表:所有用户留言信息列表,支持删除 会员列表:查看所有注册会员信息,支持删除 录入资讯:录入资讯标题、内容等信息 管理资讯&#x…

电力电子元器件行业研究:市场规模持续增长

电力电子元器件制造是指用于电能变换和控制(从而实现运动控制)的电子产品的制造。 电力电子器件具有以下特征 1)其处理电功率的能力较强;(2)其主要工作在开关状态;(3)电…

前后端分离项目,vue+uni-app+php+mysql电影院售票系统设计与实现(H5移动项目)

功能介绍 【后台功能】 会员列表:查看所有注册会员信息,支持删除 录入资讯:录入资讯标题、内容等信息 管理资讯:查看已录入资讯列表,支持删除和修改 广告设置:上传图片和设置小程序首页轮播图广告地址 留言…

Java项目:JSP网上在线酒类商城系统网站

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为前后台项目,分为普通用户与管理员两种角色,普通用户登录前台,管理员登录后台; 管理员角…

[附源码]java毕业设计医院疫情疾控管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

Android入门第34天-Android的Menu组件使用大全

简介 Android有不同的菜单: 系统菜单弹出菜单(可自定义样式)Context Menu;子菜单; 菜单的使用和我们前面说的AlertDialog很像。它可以支持自定义样式、也可以对菜单的点击事件进行绑定。 Android里有几个MainActivit…

《嵌入式 - 深入剖析STM32》STM32 启动流程详解(GCC)

开发环境: 处理器:STM32F103 GCC:10.3.1 对于我们常用的桌面操作系统而言,我们在开发应用时,并不关心系统的初始化,绝大多数应用程序是在操作系统运行后才开始运行的,操作系统已经提供了一个合…