如何使用 Laravel 队列 create/use php class 的单个实例

How to create/use a single instance of a php class with Laravel Queues

抱歉,时间太长了,但不确定如何表达我的担忧。

我正在使用 Laravel 5.8 和 php 7.2.* 我有一个 php class 像:

class PushNotificationsJob implements ShouldQueue

这用于向移动设备发送推送通知,目前运行良好,大部分情况下都很好。 由于 Apple 的限制,他们不希望您一遍又一遍地连接和断开连接,如果您在短时间内这样做太多次,他们将 return 拒绝服务 (DOS)。所以两个设计问题中的第一个是我需要能够保存一个 curl 连接以便反复使用。现在我连接,发送通知然后断开连接。问题是指示何时需要发送通知的事件不是 predictable 也无法控制。我现在遇到的第二个设计问题是我真的需要创建一个 table,其中包含每个移动设备每个通知类型的记录。我需要这样做的主要原因是我需要推迟发送某种类型的通知,直到我有 15 秒的时间,而不需要再次将该通知类型发送到该移动设备。所以我会遇到的问题是,如果每次我排队到 运行 PushNotificationsJob class 它 运行 是一个不同的实例,那么我可能最终有两个实例 运行ning 在同一时间,两者可能会查询相同的记录,并且在只应发送一次的情况下都发送相同的通知。

当然,我使用队列的原因是为了卸载从主线程发送通知的过程,这样它就不会减慢对客户端的响应。

我有一种感觉,不明白为什么会这样工作的人会建议不适用的选项,因此我会在此处添加详细信息,以备您需要更多信息时使用。发生的事情是我有相机(其中 1,000 个)在检测到运动并上传到我的服务器时正在拍照。我无法控制他们何时检测到运动并上传。当相机通过我提供的 REST api 上传照片时,我需要查找该相机的所有者并向他们发送通知。 (还有其他因素会导致发送通知,但这是最好的例子)所以问题是相机可能有 1 张照片或 50 张照片要上传。并且用户希望尽快收到通知。如果相机要上传多于 1 张照片,通常只需 5 到 8 秒即可上传下一张照片。所以我想做的是推迟发送第一个通知 15 秒,但是如果在发送第一个通知之前上传了另一张照片,那么我想将通知的计时器重置为 15 秒。因此,一旦相机超过 15 秒没有上传照片,我就会发送一个通知,而不是 50。

我的想法是,如果我能够以某种方式在另一个线程中创建 Class 的单个实例,即 运行ning 并且每次我需要发送推送通知时,我都会 "wake up" class 通过调用一些方法来告诉它它们是 activity。然后它可以重新查询 table 并处理那些要发送的 "ready" 的记录。这将解决这两个问题,这个实例可以保持与 Apple 的单个 curl 连接,并且由于只有一个实例,它可以自由查询记录并在完成后处理和删除,而不用担心与另一个实例冲突同样的事情。

这很有趣,我喜欢这个概念。

如何设置队列工作器?如果您使用 supervisor 来管理您的员工,那么这应该是一个漫长的 运行 过程。这就是为什么每次更改代码库时都必须调用 artisan queue:restart 的原因。众所周知,我会忘记这一点,最后会焦头烂额地想知道为什么更改不起作用。

基于这个事实,我想您可以将单例绑定到服务容器并在需要工作时调用它。 CURL 连接将存储在单例中,您可以在服务提供商中注册它。唯一会创建新连接的情况是队列工作线程死亡并重新启动时。

当然,您需要解决这个问题,因此它仅 运行 来自队列工作程序,而不是每次启动应用程序时。

编辑:在创建连接和绑定单例之前,我添加了一个检查以确保应用程序是来自控制台的运行(就像队列工作人员一样)。我不确定如何仅对队列工作人员执行此操作,但如果我发现了,我会通知您。目前,您只需要了解其他 artisan 命令。

App/Providers/AppServiceProvider.php

use App;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        // make sure we're running from the console 
        // and not every time the app is booted
        if (App::runningInConsole()) {

            // create your connection
            $connection = new YourCurlConnection();

            $this->app->singleton('App\Support\PushNotificationHelper', function ($app) use ($connection) {
                return new App\Support\PushNotificationHelper($connection);
            });
        }
    }
}

App\Support\PushNotificationHelper.php

class PushNotificationHelper
{
    // your CURL instance
    protected $connection;

    public function __construct($connection)
    {
        // store via dependency injection
        $this->connection = $connection;
    }

    public function sendNotification($message)
    {
        // send notification using stored connection
    }
}

App/Jobs/PushNotificationsJob.php

class PushNotificationsJob implements ShouldQueue
{
    protected $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function handle()
    {
        $message = $this->message;

        // get the singleton
        $helper = app('App\Support\PushNotificationHelper');

        // send the message using the singleton
        $helper->sendNotification($message);
    }
}

当然,如果您不想重叠连接,您需要确保 supervisor 只是 运行 一个队列工作人员,因为我认为文档列出了八个默认配置中的工作人员。

请注意,您可能希望通过调度程序偶尔触发队列的重新启动,以确保您不会在此过程中占用过多的内存。

我希望这能给你一些想法,祝你好运!