4

我正在使用 Laravel 5.5,并且正在尝试设置一些快速队列处理。我一直在一个接一个地遇到障碍。

本网站是一个雇主/雇员匹配服务。因此,当雇主发布工作职位时,它需要遍历我们系统中的所有员工并计算一些变量以确定他们与工作的匹配程度。我们已经把这一切都弄清楚了,但是当系统中有数千名员工时,一次处理一个需要很长时间。所以,我准备写几个表。第一个是定义职位 ID 和状态的简单表。第二个是列出所有员工 ID、职位 ID 和正在处理的员工状态的表格。这只需几秒钟的时间来编写,然后允许用户在应用程序中继续前进。

然后我有另一个服务器设置,每分钟运行一个 cron 来检查第一个表中的新条目。找到后,它会将其标记为已启动,然后抓取所有员工并遍历每个员工并在 Laravel 中开始排队的工作。我定义的作业确实正确提交到队列并且运行queue:work实际上确实正确处理了该作业。这都是经过测试的。

但是,我遇到的问题是我已经为队列尝试了数据库(MySQL)、Redis 和 SQS,它们都非常慢。我正在使用同一台服务器尝试操作queue:work(使用 Supervisor 并尝试运行多达 300 个进程),但随后创建了 3 个不运行 cron 但仅运行 Supervisor 的克隆(每个克隆 100 个进程)并在第一台服务器。使用数据库它会处理得很好,虽然运行 10k 个排队的作业需要几个小时,但是使用 SQS 和 Redis 我会遇到很多失败。脚本花费的时间太长或其他什么。我检查了运行工人的克隆上的 CPU,它们几乎没有达到 40%,所以我没有对服务器过度征税。

我只是在阅读有关 Horizo​​n 的信息,我不确定它是否会帮助解决这种情况。我一直在尝试查找有关如何使用 Laravel 正确设置队列处理系统的信息,并且遇到的问题多于答案。

是否有人熟悉这些东西并对如何正确设置它有任何建议,以便它非常快速且无故障(假设我的代码没有错误)?

更新:根据其他一些帖子建议,我想我会分享更多细节:

  1. 我使用 Forge 作为带有 2G RAM 的 AWS EC2 服务器的设置工具。
  2. 三个克隆中的每一个都具有以下工作器配置:

    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. 该数据库位于 Amazon RDS 上。

我很好奇 Laravel 缓存是否可以与队列系统一起使用。排队脚本的某些元素对于每次运行都是通用的,所以如果我从一开始就将这些数据排队,它可能会节省一些时间。但我不相信这会是一个巨大的进步。

4

1 回答 1

8

如果我们忽略每个作业处理的实际逻辑,只考虑运行作业的开销,Laravel 的排队系统可以轻松处理每小时 10,000 个作业,即使不是几倍,在问题中描述的环境中 - 特别是使用 Redis 后端.

对于典型的队列设置,每个盒子 100 个队列工作进程似乎非常高。除非这些作业在等待状态中花费大量时间(例如通过网络向 Web 服务发出请求并且仅使用几毫秒处理响应的作业),否则并发运行的大量进程实际上会降低性能。每个处理器内核运行一个以上的工作程序不会让我们获得太多收益。额外的工作人员会产生开销,因为操作系统必须在所有竞争进程之间划分和安排计算时间。

我检查了运行工人的克隆上的 CPU,它们几乎没有达到 40%,所以我没有对服务器过度征税。

在不了解项目的情况下,我可以建议这些工作确实会花费一些时间等待某事。您可能需要调整工作人员的数量,以找到空闲时间和过度拥挤之间的最佳平衡点。

使用数据库它会处理得很好,虽然运行 10k 个排队的作业需要几个小时,但是使用 sqs 和 redis 我会遇到很多失败。

如果您将错误消息和任何其他相关信息添加到问题中,我会尝试更新此答案。

我很好奇 Laravel 缓存是否可以与队列系统一起使用。排队脚本的某些元素对于每次运行都是通用的,所以如果我从一开始就将这些数据排队,它可能会节省一些时间。

我们当然可以在队列中执行作业时使用缓存 API。我们看到的任何性能改进都取决于为我们可以存储在缓存中的每个作业复制数据的成本。我不能确定缓存会节省多少时间,因为我不熟悉该项目,但您可以分析工作中的代码部分以查找昂贵的操作。

或者,我们可以在内存中缓存可重用的数据。当我们使用 初始化队列工作者artisan queue:work时,Laravel 会启动一个 PHP 进程并为工作者执行的所有作业启动一次应用程序。这与典型 PHP Web 应用程序的应用程序生命周期不同,其中应用程序为每个请求重新启动并在每个请求结束时处理状态。因为每个作业都在同一个进程中执行,所以我们可以创建一个对象,将共享作业数据缓存在进程内存中,也许可以通过将单例绑定到 IoC 容器中,作业可以读取甚至比 Redis 缓存存储快得多,因为我们避免了从缓存后端获取数据所需的开销。

当然,这也意味着我们需要确保我们的作业不会泄漏内存,即使我们没有像上面描述的那样缓存数据。

我只是在阅读有关 Horizo​​n 的信息,我不确定它是否会帮助解决这种情况。

Horizo​​n 提供了一项监控服务,可能有助于跟踪此设置的问题。如果应用程序使用 Horizo​​n 可以在空闲时分配工作的其他队列,它也可能会稍微提高效率,但问题似乎并不表明是这种情况。

三个克隆中的每一个都具有以下工作器配置:

command=php /home/forge/default/artisan queue:work sqs --sleep=10 --daemon --quiet --timeout=30 --tries=3

(旁注:对于 Laravel 5.3 及更高版本,该--daemon选项已弃用,该queue:work命令默认以守护程序模式运行。)

于 2017-11-09T07:01:09.877 回答