Antrean bisa menampung sejuta job, tetapi tanpa worker tidak ada satu pun yang dikerjakan. Worker adalah proses PHP CLI yang berjalan lama (long-running), berputar dalam loop mengambil job dan mengeksekusinya — konsep yang berbeda dari request HTTP yang lahir dan mati dalam hitungan detik. Perbedaan ini membawa konsekuensi operasional sendiri: worker harus dijalankan terus-menerus, diberi batas memori, dan di-restart dengan cara yang benar saat deployment. Pada chapter ini kita menjalankan worker sungguhan, membedah lifecycle-nya, mengatur prioritas antrean, lalu menutup dengan mekanisme restart yang elegan.
Menjalankan Worker #
Perintah kerjanya sederhana:
php artisan queue:workWorker memakai connection dari QUEUE_CONNECTION — database di sandbox kita — dan mengawasi antrean default. Untuk melihat kerjanya tanpa proses yang menggantung selamanya, flag --stop-when-empty membuat worker keluar begitu antrean kosong. Kita dispatch satu job lalu menjalankannya:
php artisan queue:work --stop-when-emptyOutput yang Dihasilkan:
2026-10-13 20:34:40 App\Jobs\SendWelcomeEmail ...................... RUNNING
2026-10-13 20:34:40 App\Jobs\SendWelcomeEmail ................. 18.38ms DONE
2026-10-13 20:34:43 Worker STOPPED Queue emptySetiap baris memuat timestamp, nama class job, status (RUNNING, DONE, atau FAIL), dan durasi pengerjaan — 18.38 milidetik untuk email kita. Baris terakhir mencatat alasan worker berhenti: antrean kosong. Tanpa --stop-when-empty, worker justru diam menunggu; saat idle ia tidak mencetak apa pun, hanya bangun tiap beberapa detik memeriksa antrean, dan terus hidup sampai kita menghentikannya dengan Ctrl+C.
Lifecycle Worker #
Sejak dijalankan hingga berhenti, worker menjalani pola yang tetap. Proses PHP diboot sekali di awal — framework dimuat, provider terdaftar, event listener terpasang — lalu masuk loop yang tidak membaca ulang file kode:
- Pop — worker mem-query antrean untuk job yang tersedia; bila kosong, worker
sleepselama--sleepdetik (default 3) lalu mencoba lagi. - Execute — job di-reserve (kolom
reserved_atdiisi), payload di-unserialize, laluhandle()dipanggil. - Resolve — job sukses dihapus dari antrean; job gagal dilepas kembali dengan jeda
backoff, atau dipindahkan kefailed_jobsbila percobaannya habis.
Framework yang hanya diboot sekali inilah keunggulan worker daemon: hemat waktu bootstrap untuk tiap job. Konsekuensinya juga nyata — memori tidak dilepas otomatis antar job, dan kode yang berubah setelah deployment tidak terbaca ulang sampai worker di-restart.
Prioritas Antrean #
Flag --queue menentukan antrean mana yang diawasi worker, berurutan dari yang paling diprioritaskan. Kita uji dengan dua job: PublishPost masuk antrean default duluan, SendWelcomeEmail masuk antrean emails belakangan:
use App\Models\User;
use App\Models\Post;
use App\Jobs\SendWelcomeEmail;
use App\Jobs\PublishPost;
use Illuminate\Support\Facades\DB;
$budi = User::where('email', '[email protected]')->firstOrFail();
$post = Post::firstOrCreate(['title' => 'Belajar Queue Laravel'], ['user_id' => $budi->id]);
// job default masuk duluan...
PublishPost::dispatch($post);
// ...job emails masuk belakangan
SendWelcomeEmail::dispatch($budi)->onQueue('emails');
foreach (DB::table('jobs')->orderBy('id')->get(['id', 'queue']) as $row) {
echo "job_id={$row->id} queue={$row->queue}\n";
}Output yang Dihasilkan:
job_id=1 queue=default
job_id=2 queue=emailsLalu worker dijalankan dengan php artisan queue:work --stop-when-empty --queue=emails,default:
2026-10-13 20:37:57 App\Jobs\SendWelcomeEmail ...................... RUNNING
2026-10-13 20:37:57 App\Jobs\SendWelcomeEmail ................. 19.61ms DONE
2026-10-13 20:37:57 App\Jobs\PublishPost ........................... RUNNING
2026-10-13 20:37:57 App\Jobs\PublishPost ....................... 1.51ms DONE
2026-10-13 20:38:00 Worker STOPPED Queue emptyUrutan terbalik dari urutan masuk: job di antrean emails dikerjakan lebih dulu meski baru masuk belakangan, karena emails ditulis lebih dulu di flag --queue. Log Laravel mengonfirmasi To: [email protected] terisi sebelum Post diproses: Belajar Queue Laravel. Susunan ini juga cara umum melindungi antrean penting: --queue=pembayaran,default memastikan job pembayaran tidak mengantre di belakang pekerjaan berat.
Flag Penting queue:work #
| Flag | Default | Fungsi |
|---|---|---|
--queue= | default | Antrean yang diawasi, kiri lebih diprioritaskan |
--tries= | 1 | Jumlah percobaan sebelum job dinyatakan gagal |
--timeout= | 60 | Batas detik eksekusi satu job |
--sleep= | 3 | Jeda tidur saat antrean kosong |
--memory= | 128 | Batas memori MB; worker keluar bila terlampaui |
--max-time= | 0 | Batas umur worker dalam detik |
--once | – | Proses satu job lalu keluar |
--stop-when-empty | – | Keluar saat antrean kosong |
Flag --tries dan --timeout di command line menjadi default untuk semua job yang diproses worker itu, tetapi properti job $tries, $backoff, dan $timeout di dalam class job selalu menang atas flag — pembahasan lengkapnya ada di chapter Job Retries
. Kombinasi --max-time berguna untuk pola cron yang akan kita lihat di akhir chapter, sementara --memory adalah katup pengaman kebocoran memori khas proses long-running: begitu terlampaui, worker berhenti dengan anggun dan menyerahkan pada pengawasnya untuk menghidupkan ulang.
Restart yang Elegan #
Setiap deployment kode baru, semua worker wajib di-restart agar kode terbaru terbaca. Mematikan worker secara paksa di tengah job berisiko memotong pekerjaan setengah jalan. Laravel menyelesaikannya lewat sinyal lewat cache:
php artisan queue:restartOutput yang Dihasilkan:
INFO Broadcasting queue restart signal.Perintah ini tidak menyentuh worker secara langsung — ia hanya menulis timestamp ke cache dengan kunci illuminate:queue:restart (pada cache store database, tersimpan sebagai baris dengan key laravel-cache-illuminate:queue:restart). Setiap worker mengingat timestamp saat ia lahir; di setiap putaran loop yang tidak sedang memegang job, worker membandingkan umurnya dengan timestamp di cache. Bila timestamp lebih baru, worker berhenti dengan rapi:
2026-10-13 20:26:00 Worker STOPPED Received restart signalJob yang sedang dikerjakan tetap dituntaskan sampai selesai; hanya setelah itu worker keluar. Itulah sebabnya perintah ini disebut graceful restart — kode baru efektif paling lama setelah job berjalan selesai, tanpa ada pekerjaan yang terpotong.
Banyak Worker dan Pola Pengawasan #
Satu worker memproses job satu per satu; antrean menumpuk bila job datang lebih cepat dari dikerjakan. Solusinya menjalankan beberapa proses worker sekaligus — dua worker yang mengawasi antrean sama akan membagi job di antara mereka. Di server produksi, pengawasan proses ini diserahkan pada Supervisor:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/example-com/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopwaitsecs=3600
user=example-com
numprocs=2Konfigurasi di atas (blok tampilan — membutuhkan Supervisor terpasang di server) menjalankan dua worker (numprocs=2) yang otomatis dihidupkan ulang bila mati; --max-time=3600 memutar ulang worker tiap jam sebagai pembersihan memori berkala. Alternatif tanpa Supervisor adalah cron per menit:
* * * * * cd /home/example-com && php artisan queue:work --stop-when-empty --max-time=55 >> /dev/null 2>&1Pola cron ini memakai flag yang sudah kita kenal: worker hidup maksimal 55 detik, menghabiskan antrean, lalu mati — biaya bootstrap per menit ditukar dengan proses yang selalu segar. Untuk antrean yang sudah sesat isinya, queue:clear membuang seluruh isinya tanpa mengerjakan:
php artisan queue:clear database --forceOutput yang Dihasilkan:
INFO Cleared 2 jobs from the [default] queue.Penjelasan mendalam flag worker bisa dirujuk di dokumentasi Queues . Worker kita kini hidup dan bekerja — tinggal satu pertanyaan yang belum terjawab: apa yang terjadi bila job yang ia kerjakan terus-menerus gagal? Kebijakan percobaan ulangnya kita susun di chapter berikutnya: Job Retries .