1

我们现在通过 Laravel vapor 在 AWS 无服务器基础设施上运行我们的网站。它在无服务器 Aurora 数据库上运行,并使用 Amazon SQS 作为队列驱动程序。基本上是 Laravel Vapor 部署的默认值。

我们目前遇到了一个性能问题,当我们试图解决它时,我们的头撞到了我们在旅途中遇到的其他墙壁。所以我们开始吧。

我们想做的事

我们的客户可以拥有需要续订的各种类型的订阅。这些订阅有各种风格,可以有不同的计费间隔、不同的货币和不同的价格。

在我们的客户仪表板上,我们希望向他们展示一张漂亮的卡片,告知客户他们即将进行的续订,并很好地估计费用将是多少,这样他们就可以确保他们有足够的积分:

仪表板信息

由于我们的订阅有不同的风格,我们有一个表格来保存基本订阅详细信息,例如net_renewal_date. 它还引用了subscriptionable_id其中subscriptionable_type一个与用户可以订阅的不同类型模型的变形关系。

我们的第一次尝试

在我们的第一次尝试中,我们基本上添加了一个 REST 端点来获取即将到来的更新预测。

基本上,在接下来的 30 天里,所有需要续订的订阅都被占用了。对于这些项目中的每一个,我们将以其货币计算当前价格,并添加税收计算。

然后将其返回到我们进一步用于的集合中: 1/ 计算每种货币的总数 2/ 在接下来的 14 天内过滤该集合的项目,并计算每种货币的相同总数。

然后,我们基本上只需将不同的金额转换为我们的基础货币 (EUR) 并返回其总和。

这对我们的绝大多数客户都很有效。即使是拥有 100 个订阅的客户也完全没有问题。

但随后我们将一位较大的客户迁移到了新平台。在接下来的 30 天里,他基本上有 1500 多个订阅更新,所以不太顺利......

我们的第二次尝试 因为通过上面的代码根本无法在可接受的时间内工作,我们决定我们必须将模拟计算转移到一个单独的工作中。

我们在订阅表中添加了一个属性并将其命名为“simulated_renewal_amount”

每次在以下情况下都需要运行此作业: - 价格变化 - 客户的折扣会发生变化(基于他的忠诚度,我们提供单独的价格 - 汇率变化。

所以我们的想法是监听这些变化,然后派出一个作业来重新计算任何相关订阅的模拟量。然而,这意味着例如汇率的变化可以很容易地触发 10,000 个要处理的工作。

这就是它变得棘手的地方。尽管在大多数情况下只运行一个作业只需要不到 1200 毫秒,但似乎调度大量需要为一组订阅执行相同计算的作业会导致作业在中止时运行 60 多秒。

设置这种排队作业的最佳做法是什么?我应该只创建一份工作并按顺序处理它们吗?

任何关于我们如何最好地设置它的见解,都将非常受欢迎。我们已经用它玩了很多,它似乎总是以同样的问题结束。

仅供参考 - 我们将站点托管在 laravel vapor 上,因此在 AWS 基础设施上使用 Aurora 数据库是无服务器的。

4

1 回答 1

0

我们有同样的问题。Vapor 支持多个队列,但它不允许您在每个队列的基础上设置作业并发,因此对于滴灌大量作业来说,它不是非常可配置的。我们通过制作一个播种机作业来解决这个问题,该作业从“即时作业”表中提取序列化作业。我们还添加了一个睡眠循环,以允许在整分钟内进行精细处理(每分钟安排一个新的播种机作业)。

public function handle()
{
    $killAt = Carbon::now()->addSeconds(50);

    do{
        InstantJob::orderBy('id')->cursor()->each(function(InstantJob $job){
            if($job->isThrottled()){
                return true;
            }

            $job->dispatch();
        });

        sleep(5);
    } while (Carbon::now()->lessThan($killAt));
}

油门,如果您有兴趣,可以使用油门键(作业组/名称等),看起来像:

public function isThrottled(): bool
{
    Redis::connection('cache')
        ->throttle($this->throttle_key)
        ->block(0)
        ->allow(10) //jobs
        ->every(5) // seconds
        ->then(function () use(&$throttled) {
            $throttled = false;
        }, function () use(&$throttled){
            $throttled = true;
        });

    return $throttled;
}

这实际上解决了我们在没有实际启动它们的情况下将作业滴灌到队列中的问题。

给你一个问题......我们目前正在使用一个小型 RDS 实例,我们遇到了很多并发连接过多的问题。您是否看到无服务器数据库的这个问题?他们的扩展速度是否足够快以确保没有辍学?

于 2020-01-28T11:33:39.690 回答