Pada suatu hari… saya membuat code untuk mengirimkan SMS broadcast melalui Zenziva. Seperti yang kita tau, karena ini broadcast, jadi ada kemungkinan pengiriman akan memakan waktu yang cukup lama. Makin besar jumlah user-nya, maka akan makin lama prosesnya.

Jika langsung eksekusi di web tentu saja request akan time out. Jika menggunakan set_time_limit(0), problem request time out bisa diatasi. TAPI, masa iya menunggu berjam-jam menunggu proses pengiriman SMS selesai dilakukan? :/ Belum lagi problem “connection reset” atau browser crash, atau apapunlah.

Solusinya kita bisa menggunakan queue. Queue dalam Bahasa Indonesia artinya adalah antrian. Setiap perintah yang akan dieksekusi akan dimasukkan ke antrian. Enaknya adalah, perintah ini akan dieksekusi pada proses yang berbeda di background. Sehingga kita tidak perlu menunggu http request.

Nah, tapi problem lainnya adalah saya tidak punya akses menggunakan SSH untuk install ini itu seperti Beanstalkd, Redis, Gearman, RabbitMQ, atau apapun itu yang bisa membantu proses queue ini.

Server telah di-install CPanel, tapi bukan shared hosting. Daripada minta akses SSH dan karena environment-nya seakan-akan adalah shared hosting, saya pikir lebih baik sekalian experiment :p (bukan social experiment, ntar ditabok).

How to

By default di Laravel 5, sudah ada queue driver menggunakan database. Driver ini belum ada di Laravel 4. So, tinggal ganti queue driver dan lakukan migrations.

Edit file .env atau config/queue.php, ubah driver menjadi database.

Jalankan artisan command:

php artisan queue:table
php artisan queue:failed-table

Artisan command tersebut akan membuat file di database/migrations.

Buat Job baru

php artisan make:job SmsBroadcast --queued

Ini akan membuat file baru di app/Jobs/SmsBroadcast.php

Jika menggunakan Laravel 5.0, namanya adalah Command, bukan Job. Silahkan pelajari di Laracasts mengenai Command Bus.

Sebagai contoh isi SmsBroadcast saya adalah:

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

public function handle()
{
    Zenviva::sendSms($this->phoneNumber, $this->message);
}

Lalu pada controller yang akan men-trigger pengiriman SMS

public function postSmsBroadcast()
{
    $users = User::getAllMembers();

    $smsBody = Input::get('sms_body');

    $users->each($users, function($user)
    {
        $job = new SmsBroadcast($user->phone_number, $smsBody);

        $this->dispacth($job);
    });
}

Yap, bagian pertama selesai. Sekarang jika eksekusi method tersebut, maka akan ada record baru ditambahkan ke jobs table. Tapi SMS belum benar-benar dikirimkan. Ini hanya memasukkan perintah yang akan dieksekusi ke antrian.

Untuk menjalankan perintah yang ada di antrian, kita perlu menjalankan artisan command.

php artisan queue:listen

Atau queue:work yang bisa digunakan untuk mengaktifkan daemon

Nah ini dia, karena tidak punya akses SSH di server, kita tidak bisa menjalankan queue:listen. Hmm, Jadi gimana caranya? :/

Kita bisa panggil artisan command langsung dengan menggunakan Artisan class atau facade. Misalnya Artisan::call('queue:listen'). Tapi, jika menggunakan http request, ada kemungkinan ini akan membuat seakan-akan hang. Tapi saya belum coba 😐 .

Cron for Rescue

Kita bisa manfaatkan kombinasi antara cron dan Laravel Scheduler.

Tambahkan ke app/Console/Kernel.php

protected function schedule(Schedule $schedule)
{
    $schedule->command('queue:work')
             ->everyMinute()
             ->withoutOverlapping();
}

Yap, selesai. Setiap menit akan memeriksa antrian dan sms akan dikirimkan 😉

Jika ada pertanyaan, saran atau cara yang lebih baik–langsung aja isi kolom komentar.

Iklan

10 pemikiran pada “Laravel 5 – Trik Menggunakan Queue tanpa akses SSH

  1. thanks for great tutorial.

    btw mas, berdasarkan kode diatas, berarti setiap sms yg dikirim ke user itu per menit ya? atau gmn?

    1. Hoo, nggak. SMS akan dikirimkan hingga antrian kosong.

      Yang jalan per menit itu adalah si queue:work. Berikutnya kita sebut aja worker supaya lebih mudah.

      Logikanya jika worker sedang jalan, lalu ternyata membutuhkan waktu 5 menit untuk selesai, maka akan jalan 5 worker.

      Nah, tapi di contoh kita pakai ->withoutOverlapping(), sehingga worker berikutnya akan menunggu worker sebelumnya selesai. Tapi karena worker pertama sudah mengosongkan antrian, jadi worker berikutnya hanya melakukan pengecekan apakah ada antrian atau tidak.

      1. Hmm, tergantung kasusnya ini. Kalau menjalankan queue:worker, sebaiknya hanya satu worker. Karena cara kerja si worker ini mengosongkan queue.

        Tapi kalau kasusnya berbeda, kita bisa jalankan pada detik berikutnya. Misalnya dengan menggunakan ->cron('* * * * *');. Caranya sama dengan format cron biasa.
        Contoh:

        protected function schedule(Schedule $schedule)
        {
            $schedule->exec('ls -la /')->cron('* * * * *');
        }
        
  2. om, yang menyebabkan schedule gak jalan apa ya?
    ini saya lagi nyoba2 pake everyMinutes(), tapi saya tungguin gak jalan2..tapi karena saya masih dapet akses ssh, jadi saya jalanin artisan schedule:run..nah gimana jalaninnya klo misalkan bener2 gak dapet akses ssh kayak kasus ini?

Tinggalkan Balasan

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s