AmooAti
AmooAti

Reputation: 1170

Send large number of notifications to users - Laravel

I want to send a large number of notifications in Laravel.

The notification method is firebase and I'm using kutia-software-company/larafirebase package as Notifiable.

Currently, I have about 10000 fcms but I want to optimize sending notifications to 100000 fcms.

I implemented a system like below:

First, I created a notifiable class as described in the package

<?php

namespace Vendor\Core\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Kutia\Larafirebase\Messages\FirebaseMessage;

class FirebasePushNotification extends Notification
{
    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct(protected $title, protected $message, protected array $fcmTokens = [])
    {
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['firebase'];
    }

    public function toFirebase($notifiable)
    {
        return (new FirebaseMessage)
            ->withTitle($this->title)
            ->withBody($this->message)
            ->withPriority('high')
            ->asNotification($this->fcmTokens);
    }
}

Second, Created a Job for notifications:

<?php

namespace Vendor\PushNotification\Jobs;

use Vendor\Core\Notifications\FirebasePushNotification;
use Vendor\Customer\Contracts\UserDeviceInfo;
use Vendor\Customer\Contracts\Customer;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Notification;


class SendPushNotificationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable;

    public function __construct(public string $title, public string $body, public Customer $customer, public UserDeviceInfo $userDeviceInfo)
    {
    }

    public function handle()
    {
        Notification::send($this->customer, new FirebasePushNotification($this->title, $this->body, [$this->userDeviceInfo->fcm]));
    }

}

And using Bus::dispatch for sending jobs to queue:

        $usersDeviceInfos = $usersDeviceInfos->with('customer')->get();

        $bus = [];
        for ($i = 0; $i < 100000; $i++){
            foreach ($usersDeviceInfos as $usersDeviceInfo) {

                $bus [] = new SendPushNotificationJob($data['notification_title'], $data['notification_body'], $usersDeviceInfo->customer, $usersDeviceInfo);
            }
        }

        Bus::batch($bus)->name('push notification' . now()->toString())->dispatch();

Currenly, because of development environement I have just one fcm and use a loop to simulate 10000, It takes more than a minute to run this code and I'll get Maximum execution error.

Also, note that I configured my queue and it's not sync and I checked these two questions but didn't help:

Hope to find a faster method to batch those jobs and send them to queue

Upvotes: 1

Views: 2072

Answers (1)

AmooAti
AmooAti

Reputation: 1170

I found a solution that can handle sending notifications up to one million fcms in a few seconds

I decided to use kreait/laravel-firebase and use multicast that was mentioned here

First I chunk fcms then I use A job and dispatch them to the queue.

Here's an example of the solution:

<?php

namespace Vendor\PushNotification\Jobs;

use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Laravel\Firebase\Facades\Firebase;

class SendPushNotificationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable;

    public function __construct(public string $title, public string $body, public array $fcmTokens)
    {
    }

    public function handle()
    {
        $message = CloudMessage::fromArray(YOUR_DATA);
        Firebase::messaging()->sendMulticast($message, $this->fcmTokens);
    }

}
        $chunks = $usersDeviceInfos->with('customer')->get()->pluck('fcm')->chunk(500);

        foreach ($chunks as $chunk) {
            dispatch(new SendPushNotificationJob($data['notification_title'], $data['notification_body'], $chunk->toArray()));
        }

Upvotes: 3

Related Questions