Laravel队列 - 如何设置快速处理器

4
我正在使用Laravel 5.5,尝试设置一些快速的队列处理。但是我遇到了一个又一个的障碍。
这个站点是一个雇主/员工匹配服务。所以当雇主发布职位时,它需要运行我们系统中所有员工,并计算许多变量来确定他们与职位的匹配程度。我们已经解决了这个问题,但是在系统中有成千上万的员工时,逐个处理需要很长时间。因此,我设置了写入几个表。第一个是简单的表,定义了位置ID和状态。第二个是列出所有员工ID、位置ID和正在处理的员工状态的表。这只需要几秒钟就可以写好,然后允许用户在应用程序中继续操作。
然后,我另外设置了一台服务器,每分钟运行一个cron检查第一个表中的新条目。找到后,将其标记为已启动,然后获取所有员工并逐个运行队列作业。我定义的作业确实能正确提交到队列,并且运行queue:work也能够正确处理作业。这已经过测试。
然而,我遇到的问题是,我尝试使用数据库(MySQL)、Redis和SQS作为队列,它们都非常慢。我曾经使用同一台服务器尝试运行queue:work(使用Supervisor并尝试运行多达300个进程),但然后创建了3个克隆体,不运行cron而只运行Supervisor(每个克隆体100个进程),并在第一台服务器上杀死了Supervisor。使用数据库可以处理好,但是要通过10k个排队作业需要几个小时,但是使用SQS和Redis我得到了大量的失败。脚本太长时间或其他原因。我检查了运行工人的克隆体的CPU,它们仅达到40%,因此我没有过度使用服务器。
我刚刚读到Horizon,我不确定它是否会有所帮助。我一直在尝试寻找关于如何正确设置Laravel队列处理系统的信息,但只遇到更多问题而不是答案。
是否有人熟悉这方面的知识,并有任何建议如何正确设置,使其非常快速且无故障(假设我的代码没有错误)?
更新:根据其他帖子的建议,我想分享更多细节:
  1. I'm using Forge as the setup tool with AWS EC2 servers with 2G of RAM.
  2. Each of the three clones has the following worker configuration:

    command=php /home/forge/default/artisan queue:work sqs --sleep=10 --daemon --quiet --timeout=30 --tries=3  
    
    process_name=%(program_name)s_%(process_num)02d  
    autostart=true  
    autorestart=true  
    stopasgroup=true  
    killasgroup=true  
    user=forge  
    numprocs=100  
    stdout_logfile=/home/forge/.forge/worker-149257.log
    
  3. The database is on Amazon RDS.

我想知道Laravel缓存是否能与队列系统一起工作。有些队列脚本的元素在每次运行时都是相同的,因此如果从开始就将这些数据排队可能会节省一些时间。但我不确定它会有很大的改进。


根据问题描述和复杂程度,我建议您在larachat slack团队上发布您的问题。如果您找到了答案,也请在此处发布。 - Suraj
我在Laravel方面没有经验,但是几年前我们在工作中有一个PHP项目,使用beanstalkd作为消息队列,每小时愉快地执行了数百万个任务。如果执行10,000个任务需要几个小时的时间,也许你的匹配算法太复杂或者性能不够好? - rickdenhaan
安德鲁,你在Redis/SQS后端看到了哪些错误?你在queue:work上使用了--timeout=30,这意味着作业将在30秒后中止。如果在一个盒子上同时运行大量繁重的作业(100似乎有点高),它们可能会超时。 - Cy Rossignol
1个回答

10
如果我们忽略每个任务实际处理的逻辑,并且只考虑运行任务本身所需的开销,那么在描述中提到的环境下,特别是使用 Redis 后端,Laravel 的队列系统可以轻松地处理每小时 10,000 个及以上的任务。
对于典型的队列设置来说,每个计算机上的 100 个队列工作进程似乎相当高。除非这些任务花费了大量时间处于等待状态,例如发送跨网络的 Web 服务请求并仅使用几毫秒处理响应的任务,否则同时运行的进程数量实际上会降低性能。运行多个进程不如每个处理器内核运行一个工作进程。额外的工作进程会带来开销,因为操作系统必须在所有竞争进程之间分配和调度计算时间。
不了解项目的情况下,我建议您尝试调整 worker 的数量,以找到空闲时间和拥挤之间的平衡点。
在执行队列中的作业时,我们确实可以使用缓存 API。我们看到的任何性能改进取决于重现缓存数据的成本。我无法确定缓存可以节省多少时间,因为我不熟悉该项目,但您可以对作业中的代码部分进行分析,以查找昂贵的操作。
或者,我们可以在内存中缓存可重用的数据。当使用`artisan queue:work`初始化队列工作进程时,Laravel 启动一个 PHP 进程并为该 worker 执行的所有作业一次性引导应用程序。这与典型的 PHP Web 应用程序的应用程序生命周期不同,在该生命周期中,应用程序会为每个请求重新启动,并在每个请求结束时销毁状态。由于每个作业都在同一个进程中执行,因此我们可以创建一个对象,将共享作业数据缓存在进程内存中,例如通过将单例绑定到 IoC 容器中,作业可以比甚至 Redis 缓存存储快得多地读取数据,因为我们避免了从缓存后端获取数据所需的开销。
当然,这也意味着我们需要确保我们的作业不泄漏内存,即使我们不像上面描述的那样缓存数据。

我刚刚了解了Horizon,不确定它是否有助于解决这个问题。

Horizon提供了一个监控服务,可以帮助跟踪此设置的问题。如果应用程序在空闲时使用其他队列,Horizon可以在它们之间分配任务,这可能会稍微提高效率,但该问题似乎并未表明这种情况。

Each of the three clones has the following worker configuration:

command=php /home/forge/default/artisan queue:work sqs --sleep=10 --daemon --quiet --timeout=30 --tries=3
(旁注:对于 Laravel 5.3 及更高版本,--daemon 选项已过时,queue:work 命令默认在守护进程模式下运行。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接