← Semua pembelajaran / Laravel Nol → Enterprise
Fase 8 · FrankenPHP & RoadRunner

Pool & supervisor RoadRunner

Di sinilah RoadRunner menonjol. Pool-nya punya pengawas bawaan yang bisa membunuh worker yang bocor, memutus permintaan yang menggantung, dan menambah worker saat beban naik — semuanya deklaratif.

Intisari

  • num_workers menentukan konkurensi; nol berarti sejumlah CPU logis.
  • Supervisor mengawasi TTL, waktu menganggur, memori, dan durasi eksekusi.
  • Batas lunak menunggu permintaan selesai; batas keras (exec_ttl) memutusnya.
  • max_queue_size menolak permintaan berlebih alih-alih mengantre tanpa batas.
  • dynamic_allocator menambah worker sementara saat lonjakan, lalu menguranginya lagi.

Konfigurasi pool lengkap

pool:
  # Berapa proses PHP yang dijalankan. 0 = jumlah CPU logis.
  num_workers: 8

  # Restart worker setelah sekian permintaan. 0 = tanpa batas.
  max_jobs: 500

  # Tolak permintaan kalau antrean internal sudah sepanjang ini. 0 = tanpa batas.
  max_queue_size: 100

  allocate_timeout: 60s
  destroy_timeout: 60s

  # Menambah worker sementara saat semua sedang sibuk
  dynamic_allocator:
    max_workers: 25
    spawn_rate: 10
    idle_timeout: 10s

  supervisor:
    watch_tick: 5s
    ttl: 0s                    # umur maksimum worker (lunak)
    idle_ttl: 10s              # lama menganggur sebelum dimatikan (lunak)
    max_worker_memory: 256     # MB (lunak)
    exec_ttl: 60s              # durasi maksimum satu permintaan (KERAS)

Batas lunak versus keras

BatasJenisPerilaku saat terlampaui
ttlLunakWorker dimatikan setelah permintaannya selesai
idle_ttlLunakWorker yang menganggur dimatikan
max_worker_memoryLunakWorker diganti setelah permintaan selesai
exec_ttlKerasPermintaan diputus di tengah jalan

exec_ttl adalah pengaman yang paling berharga di produksi. Ia memutus permintaan yang menggantung — misalnya karena panggilan API luar tanpa timeout — sehingga worker itu kembali tersedia. Tanpa itu, sepuluh permintaan macet pada delapan worker berarti aplikasi berhenti melayani siapa pun, dan tidak ada apa pun di dalam PHP yang bisa memulihkannya.

max_jobs versus max_worker_memory

Keduanya menangani masalah yang sama — kebocoran memori — dengan cara berbeda. Dokumentasi RoadRunner memberi saran yang tegas: kalau tujuanmu mengendalikan memori, pakai supervisor.max_worker_memory, bukan pool.max_jobs.

max_jobsmax_worker_memory
PemicuJumlah permintaanPemakaian memori sesungguhnya
Restart yang tidak perluSering — worker sehat pun ikutHanya yang memang membengkak
Melindungi dari kebocoran cepatKurang — bisa meledak sebelum hitungannya tercapaiYa
Mudah diprediksiYaBergantung beban

Pola yang sehat: pasang max_worker_memory sebagai pertahanan utama, dan max_jobs dengan angka besar sebagai jaring pengaman kedua untuk kebocoran yang bukan berupa memori — misalnya file descriptor yang tidak pernah ditutup.

Penskalaan dinamis

pool:
  num_workers: 4                # selalu ada
  dynamic_allocator:
    max_workers: 25             # boleh naik sampai sini
    spawn_rate: 10              # berapa yang dibuat sekaligus saat kekurangan
    idle_timeout: 10s           # dikurangi lagi setelah menganggur selama ini

Manfaatnya: kamu tidak perlu memesan memori untuk 25 worker sepanjang waktu, tapi tetap sanggup menyerap lonjakan. Yang perlu diperhatikan — menyalakan worker PHP baru bukan operasi gratis; ia harus mem-bootstrap aplikasi dari nol. Untuk lonjakan yang sangat tiba-tiba, menaikkan num_workers lebih dapat diandalkan.

Berapa worker yang tepat

Sifat bebanSaran dokumentasi
Terikat CPUPantau beban CPU, targetkan 90–95%; sisakan sedikit untuk GC Go
Terikat I/OSebanyak yang muat di memori — worker itu murah
UmumSetara jumlah utas CPU sebagai titik awal
Referensi memoriWorker "hello world" memakai sekitar 26 MB RSS

26 MB itu untuk worker kosong. Worker Laravel yang sudah mem-bootstrap seluruh aplikasi beserta paket-paketnya jauh lebih besar — ukur sendiri dengan ./rr workers -i. Angka itulah yang harus kamu kalikan dengan num_workers untuk memastikan muat di dalam jatah memori kontainer.

Antrean internal

pool:
  max_queue_size: 100

Saat semua worker sibuk, permintaan menunggu di antrean internal. Tanpa batas, antrean itu tumbuh sampai pengguna sudah menyerah menunggu — servermu tetap mengerjakan permintaan yang tidak ada lagi yang menunggunya. Dengan batas, permintaan berlebih ditolak seketika, dan load balancer bisa mengarahkannya ke kontainer lain. Menolak cepat lebih baik daripada lambat merata.

Mode pengembangan

pool:
  debug: true      # tidak pre-alokasi; satu worker dibuat saat permintaan datang

Dalam mode ini, worker dibuat baru untuk tiap permintaan — artinya perubahan kodemu langsung terlihat, seperti PHP-FPM. Sangat berguna saat mengembangkan; jangan pernah dipakai di produksi, karena seluruh keuntungan worker mode hilang.

Rekomendasi produksi

  1. Jangan dengarkan RPC di 0.0.0.0 kecuali memang perlu lintas kontainer.
  2. relay: pipes — sedikit lebih cepat daripada soket Unix.
  3. Setel memory_limit PHP 10–20% di bawah max_worker_memory.
  4. opcache.enable_cli=1 — worker adalah proses CLI.
  5. Pakai endpoint health check saat berjalan di lingkungan cloud.
  6. Hindari image berbasis Alpine — musl tidak sepenuhnya cocok dengan biner Go RoadRunner dan sebagian ekstensi PHP.
  7. Koneksi keep-alive memberi peningkatan performa yang besar; pastikan load balancer memakainya.

Latihan: setel max_worker_memory: 128 dan buat rute yang sengaja mengisi array statis sedikit demi sedikit. Panggil berulang sambil memantau ./rr workers -i, dan amati supervisor mengganti worker saat ambang batasnya terlampaui.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.