我正在使用Laravel 5.5,我正在尝试设置一些快速队列处理。我一直在遇到一个障碍。
此网站是雇主/员工匹配服务。因此,当雇主发布工作职位时,需要运行我们系统中的所有员工并计算一些变量以确定他们与工作的匹配程度。我们已经弄明白了,但是当系统中有数千名员工时,需要花费很长时间来处理一个。所以,我开始写几个表。第一个是定义位置ID和状态的简单表。第二个是列出所有员工ID,职位ID以及正在处理的员工的状态的表。这只需要几秒钟的时间来编写,然后允许用户在应用程序中继续前进。
然后我有另一个服务器设置来每分钟运行一个cron,检查第一个表中的新条目。找到后,它会将其标记为已启动,然后抓住所有员工并遍历每位员工并在Laravel中启动排队作业。我定义的作业正确地提交到队列,并且正在运行queue:work
确实正确地处理了该作业。这都经过了测试。
然而,我遇到的问题是我为队列尝试了数据库(MySQL),Redis和SQS,它们都非常慢。我使用同一台服务器尝试操作queue:work
(使用Supervisor并尝试运行多达300个进程),但随后创建了3个不运行cron但只运行Supervisor的克隆(每个克隆100个进程)并在第一台服务器上杀死了Supervisor。使用数据库它会处理正常,但要运行10k排队的工作需要几个小时,但是使用SQS和Redis我会遇到大量失败。脚本花了太长时间或其他东西。我检查了运行工作人员的克隆上的CPU,他们几乎没有达到40%,所以我不会对服务器过度征税。
我刚刚读到Horizon,我不确定它是否有助于这种情况。我一直在努力寻找有关如何使用Laravel正确设置队列处理系统的信息,并且只是继续遇到问题而不是答案。
是否有人熟悉这些内容并对如何正确设置这些内容有任何建议,以便它非常快且无故障(假设我的代码没有错误)?
更新:根据其他一些建议,我想我会分享更多细节:
三个克隆中的每一个都具有以下工作人员配置:
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
数据库位于Amazon RDS上。
我很好奇Laravel缓存是否可以与队列系统一起使用。排队脚本的元素对于每次运行都是通用的,所以如果我从头开始排队数据,可能会节省一些时间。但我不相信这将是一个巨大的进步。
答案 0 :(得分:3)
如果我们忽略每个作业处理的实际逻辑,并考虑单独运行作业的开销,Laravel的排队系统可以轻松地每小时处理10,000个作业,如果不是那么几次,在环境中在问题中描述 - 尤其是Redis后端。
对于典型的队列设置,每个框100个队列工作进程似乎非常高。除非这些作业在等待状态中花费大量时间 - 例如通过网络向Web服务发出请求并且仅使用几毫秒处理响应的作业 - 同时运行大量进程实际上会降低表现。通过为每个处理器核心运行多个工作程序,我们不会获得太多收益。额外的工作人员会产生开销,因为操作系统必须在所有竞争过程之间划分和安排计算时间。
我检查了运行工作人员的克隆上的CPU,他们几乎没有达到40%,所以我不会对服务器过度征税。
在不知道该项目的情况下,我可以建议这些工作可能会花费一些时间等待某些事情。您可能需要调整工人数量,以便在空闲时间和过度拥挤之间找到最佳位置。
使用数据库它会处理正常,但要运行10k排队的工作需要几个小时,但是使用sqs和redis我会遇到很多失败。
如果您在问题中添加错误消息和任何其他相关信息,我将尝试更新此答案。
我很好奇Laravel缓存是否可以与队列系统一起使用。排队脚本的元素对于每次运行都是通用的,所以如果我从头开始将这些数据排队,可能会节省一些时间。
我们当然可以在队列中执行作业时使用缓存API。我们看到的任何性能改进取决于我们可以存储在缓存中的每个作业的再现数据的成本。我不能肯定如何多长时间缓存可以节省,因为我不熟悉该项目,但您可以在工作中分析代码的各个部分以查找昂贵的操作。
或者,我们可以在内存中缓存可重用的数据 。当我们使用artisan queue:work
初始化队列工作程序时,Laravel启动一个PHP进程并为所有工作人员执行的作业启动应用程序一次。这与典型的PHP Web应用程序的应用程序生命周期不同,其中应用程序重新启动每个请求并在每个请求结束时处置状态。因为每个作业都在同一个进程中执行,所以我们可以创建一个对象来缓存进程内存中的共享作业数据,可能是通过将单例绑定到IoC容器中,这些作业可以比Redis缓存存储更快地读取,因为我们避免了从缓存后端获取数据所需的开销。
当然,这也意味着我们需要确保我们的工作不会泄漏内存,即使我们没有如上所述缓存数据。
我刚刚读到了Horizon,我不确定它是否有助于这种情况。
Horizon提供的监控服务可能有助于跟踪此设置的问题。如果应用程序使用Horizon可以在空闲时分配工作的其他队列,它也可以提高效率,但问题似乎并不表明情况就是这样。
三个克隆中的每一个都具有以下工作人员配置:
command=php /home/forge/default/artisan queue:work sqs --sleep=10 --daemon --quiet --timeout=30 --tries=3
(旁注:对于Laravel 5.3及更高版本,不推荐使用--daemon
选项,默认情况下queue:work
命令以守护进程模式运行。)