Memindahkan kerja berat ke latar belakang dengan yiisoft/yii2-queue: konsep job dan push, memilih driver antara Redis, DB, AMQP, atau SQS, menjalankan worker secara manual maupun via supervisor, serta menangani retry, timeout, dan scheduling tugas seperti kirim email dan laporan.

Di episode 11 kalian membuat aplikasi merespons lebih cepat dengan cache. Tapi ada kelas masalah yang tidak bisa diselesaikan cache: kerja berat yang tidak boleh menghambat request. Kirim email ke ribuan pelanggan, generate laporan Excel 500 MB, sinkronisasi ke sistem ERP — jika semua dilakukan di dalam request HTTP, user menunggu sampai timeout.
Solusinya adalah queue: pekerjaan dimasukkan ke antrean, request langsung selesai, dan worker terpisah memprosesnya di latar belakang. Di episode ini kita memakai yiisoft/yii2-queue — ekstensi resmi Yii yang mendukung banyak backend — dari instalasi sampai penjadwalan worker di production.
Tiga aktor dalam sistem queue:
execute().Ini membuat request web tetap ringan: user submit form "kirim email ke semua pelanggan", request hanya memasukkan job dan membalas "sedang diproses", sementara worker mengerjakan pengiriman satu per satu di belakang layar.
composer require yiisoft/yii2-queueKonfigurasi di config/console.php (tempat worker berjalan) dan config/web.php (untuk push dari web):
'components' => [
'queue' => [
'class' => \yii\queue\db\Queue::class,
'db' => 'db',
'tableName' => '{{%queue}}',
'channel' => 'default',
'mutex' => \yii\mutex\MysqlMutex::class,
],
],Jangan lupa buat tabel queue-nya:
php yii migrate/up --migrationPath=@yii/queue/db/migrations| Driver | Kapan Dipakai | Sifat |
|---|---|---|
yii\queue\db\Queue | Setup paling sederhana, satu database | Persist di MySQL/PostgreSQL |
yii\queue\redis\Queue | Sudah punya Redis, banyak worker | Cepat; transient tanpa persistence |
yii\queue\amqp\Queue | Sudah memakai RabbitMQ/AMQP | Fitur lengkap, routing, dead-letter |
yii\queue\sqs\Queue | Infrastruktur AWS | Managed queue, scaling otomatis |
Keputusan simpel: mulai dengan db saat belajar; pindah ke redis atau sqs saat traffic naik. Contoh konfigurasi Redis:
'queue' => [
'class' => \yii\queue\redis\Queue::class,
'redis' => 'redis',
'channel' => 'default',
],Job adalah class sederhana dengan method execute():
<?php
namespace app\jobs;
use Yii;
use yii\base\BaseObject;
use yii\queue\JobInterface;
use yii\queue\RetryableJobInterface;
class SendEmailJob extends BaseObject implements JobInterface, RetryableJobInterface
{
public string $to;
public string $subject;
public string $body;
public function execute($queue): void
{
Yii::info("Mengirim email ke {$this->to}", __METHOD__);
$mailer = Yii::$app->mailer->compose()
->setTo($this->to)
->setSubject($this->subject)
->setHtmlBody($this->body);
if (!$mailer->send()) {
throw new \RuntimeException("Gagal mengirim email ke {$this->to}");
}
}
public function getTtr(): int
{
return 60; // 60 detik sebelum job dianggap timeout
}
public function canRetry($attempt, $error): bool
{
return $attempt < 3; // maksimal 3 kali percobaan
}
}Yang perlu diperhatikan:
getTtr() (Time To Reserve): berapa lama worker boleh mengerjakan job sebelum dianggap gagal.canRetry(): menentukan berapa kali job dicoba ulang jika gagal.to, subject, body) diserialisasi ke queue — jangan simpan object berat atau resource di dalamnya. Simpan ID saja, lalu muat ulang di execute().Dari mana saja — controller, command, atau event handler:
use app\jobs\SendEmailJob;
public function actionSendNewsletter(): string
{
$subscribers = Subscriber::find()->select('email')->column();
foreach ($subscribers as $email) {
Yii::$app->queue->push(new SendEmailJob([
'to' => $email,
'subject' => 'Newsletter Mingguan',
'body' => $this->renderPartial('newsletter'),
]));
}
Yii::$app->session->setFlash(
'success',
'Newsletter masuk antrean: ' . count($subscribers) . ' penerima.'
);
return $this->redirect(['site/index']);
}Satu poin penting: karena properties di-serialisasi saat push, pastikan semua property bisa di-serialize (string, int, array). Kalau job butuh data dari model, push ID-nya dan muat model di dalam execute() — ini juga menjaga data selalu segar saat job benar-benar dieksekusi.
Worker dijalankan sebagai console process:
php yii queue/listen 3 # ambil job tiap 3 detikqueue/listen adalah mode yang dianjurkan: worker selalu hidup dan memproses job begitu masuk. Untuk production, worker dijalankan di belakang supervisor atau systemd (detail di episode 22), bukan manual:
[program:yii-queue]
command=/usr/bin/php /var/www/app/yii queue/listen 3
directory=/var/www/app
numprocs=2
autostart=true
autorestart=true
stderr_logfile=/var/www/app/runtime/logs/queue.lognumprocs=2 artinya dua proses worker paralel — jumlah ini bisa dinaikkan sesuai kapasitas server dan panjang antrean.
Tip
Saat development, jalankan worker sekali jalan dengan php yii queue/run 3 — ia memproses semua job di antrean lalu keluar. Ini sangat berguna di pipeline CI dan untuk tes manual, karena tidak ada proses yang menggantung.
Worker listen memproses job segera, tapi ada kategori tugas yang harus jalan berdasarkan waktu — bukan dari event aplikasi. Contoh: laporan harian, pembersihan file basi, reminder email.
Pendekatan paling umum: cron men-jadwalkan perintah konsol yang push job:
# /etc/crontab
0 3 * * * www-data cd /var/www/app && php yii report/daily >/dev/null 2>&1
0 */4 * * * www-data cd /var/www/app && php yii maintenance/cleanup >/dev/null 2>&1Dan perintah konsolnya:
<?php
namespace app\commands;
use app\jobs\GenerateDailyReportJob;
use Yii;
use yii\console\Controller;
class ReportController extends Controller
{
public function actionDaily(): int
{
Yii::$app->queue->push(new GenerateDailyReportJob([
'date' => date('Y-m-d', strtotime('-1 day')),
]));
$this->stdout("Laporan harian masuk antrean\n");
return self::EXIT_CODE_NORMAL;
}
}Pola ini memisahkan dua hal: cron hanya memicu (ringan, cepat), dan worker yang mengerjakan (berat, di background). Jika laporan harian mulai memakan 30 menit, cron tidak akan timeout karena yang diblokir bukan cron.
Queue butuh pengawasan. Cek status lewat perintah:
php yii queue/info # jumlah job per status (db driver)
php yii queue/status <id> # status job tertentuUntuk Redis: redis-cli llen queue:default menunjukkan panjang antrean. Di production, pantau tiga metrik: panjang antrean (membengkak = worker kurang), umur job (antrean lama = backlog), dan jumlah retry (tinggi = job error terus-menerus dan perlu diperiksa).
Inti yang harus dibawa pulang:
db saat awal, redis/sqs saat traffic naik.execute(); implementasikan RetryableJobInterface untuk getTtr() dan canRetry().php yii queue/listen.Di episode 13 selanjutnya, kita belajar melihat ke dalam aplikasi: logging, debugging & profiling — mengirim log ke berbagai target dengan kategori yang jelas, membedah request dengan Yii Debug Toolbar, serta instrumentasi dengan beginProfile/endProfile untuk menemukan bottleneck performa. Sampai jumpa di episode 13!