【问题标题】:Managing Queued jobs / Database connections / performance on Laravel Vapor在 Laravel Vapor 上管理排队作业/数据库连接/性能
【发布时间】:2020-01-04 02:02:10
【问题描述】:

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

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

我们想做的事

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

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

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

我们的第一次尝试

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

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

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

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

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

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

我们的第二次尝试 由于上述代码无法在可接受的时间内完成,因此我们决定必须将模拟计算转移到单独的工作中。

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

每次出现以下情况时,该作业都需要运行: - 价格变化 - 客户的折扣会改变(根据他的忠诚度,我们提供单独的价格 - 汇率变化。

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

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

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

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

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

【问题讨论】:

    标签: laravel amazon-web-services amazon-sqs jobs laravel-vapor


    【解决方案1】:

    我们遇到了同样的问题。 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 实例,并且我们遇到了很多并发连接过多的问题。您是否看到无服务器数据库的这个问题?它们的扩展速度是否足够快以确保不会退出?

    【讨论】:

      猜你喜欢
      • 2016-09-12
      • 2022-08-11
      • 1970-01-01
      • 1970-01-01
      • 2015-11-03
      • 1970-01-01
      • 2020-10-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多