RoadRunner di produksi
Halaman produksi RoadRunner berisi saran yang sangat konkret dan sebagian mengejutkan. Membacanya sebelum deploy pertama menghemat banyak jam debugging yang sulit.
Intisari
- State dan memori tidak dibagi antar worker, tapi dibagi di dalam satu worker.
- Tutup semua descriptor, terutama saat exception fatal.
gc_collect_cycles()menekan memori tapi memperlambat eksekusi secara signifikan โ pakai dengan hati-hati.- Koneksi keep-alive memberi peningkatan performa sekitar 40%.
- Hindari image berbasis Alpine โ musl tidak sepenuhnya kompatibel dengan biner Go RoadRunner dan banyak ekstensi PHP.
State dan memori
Aturan dasarnya persis seperti yang kita bahas di materi jebakan state, dinyatakan ulang dengan tegas oleh dokumentasi RoadRunner: state tidak dibagi antar worker, tetapi dibagi di dalam satu worker. Dua konsekuensi praktisnya:
| Konsekuensi | Artinya |
|---|---|
| Tidak dibagi antar worker | Jangan andalkan cache dalam memori untuk konsistensi โ worker lain tidak melihatnya |
| Dibagi di dalam satu worker | Data permintaan sebelumnya bisa bocor ke permintaan berikutnya |
Saran konkret dari dokumentasinya:
- Tutup semua descriptor โ terutama pada jalur exception fatal.
- Waspadai kebocoran memori; bersikap selektif terhadap komponen yang dipakai. Worker akan di-restart saat bocor, tapi lebih baik dirancang supaya tidak bocor sama sekali.
- Hindari polusi state โ jangan menyimpan data global atau data pengguna di memori.
- Koneksi database dan setiap pipa atau soket adalah titik kegagalan potensial.
Soal menutup koneksi database setiap iterasi: dokumentasinya menyebut ini sebagai cara mudah menangani koneksi yang bermasalah, sekaligus mengakui bahwa itu bukan solusi yang paling berperforma. Menutup dan membuka ulang koneksi setiap permintaan menghapus salah satu keuntungan terbesar worker mode. Pilihan yang lebih baik: biarkan koneksi tetap hidup, dan tangani koneksi yang putus dengan mekanisme reconnect yang sudah disediakan Laravel.
gc_collect_cycles()
// Di dalam loop worker, setelah setiap permintaan
gc_collect_cycles();
Dokumentasi RoadRunner memberi peringatan tegas di sini: memanggil
gc_collect_cycles() setiap eksekusi memang menekan pemakaian memori, tapi
memperlambat eksekusi secara signifikan. Jangan memasangnya sebagai kebiasaan. Pasang hanya
kalau kamu benar-benar melihat memori menumpuk karena referensi melingkar โ dan ukur dampaknya terhadap
throughput setelah memasangnya.
Menentukan jumlah worker
| Sifat beban | Panduan resmi |
|---|---|
| Umum | Jumlah worker = jumlah utas CPU |
| Terikat I/O | Sebanyak yang muat di memori; worker itu murah |
| Terikat CPU | Pantau beban rata-rata, targetkan 90โ95%, sisakan untuk GC Go |
| Latensi hampir tetap | Hitung dari target throughput dan latensi per permintaan |
worker โ throughput target (req/detik) ร latensi rata-rata (detik)
Contoh: 200 req/detik ร 0,05 detik = 10 worker (ditambah margin)
Jaringan dan koneksi
- Hubungkan worker lewat pipa; soket Unix sedikit lebih lambat.
- Jangan mendengarkan RPC di
0.0.0.0kecuali di dalam Docker yang memang butuh. - Keep-alive memberi peningkatan performa sekitar 40% โ pastikan lapisan di depan memakainya.
- Pakai endpoint health check saat berjalan di lingkungan cloud.
Angka 40% itu layak diperiksa di sisi load balancer-mu. Kalau ALB atau Nginx menutup koneksi setelah setiap permintaan, kamu membayar biaya pembukaan koneksi TCP untuk setiap permintaan โ dan kehilangan sebagian besar keuntungan yang tadi susah payah kamu kejar lewat worker mode.
Memori dan OPcache
; php.ini
memory_limit = 220M ; 10โ20% di bawah max_worker_memory (256M)
opcache.enable = 1
opcache.enable_cli = 1 ; WAJIB โ worker adalah proses CLI
opcache.validate_timestamps = 0
Kenapa memory_limit harus di bawah max_worker_memory. Kalau PHP mencapai
batasnya lebih dulu, kamu mendapat error fatal PHP di tengah permintaan pengguna. Kalau supervisor RoadRunner
yang mencapainya lebih dulu, worker diganti dengan rapi setelah permintaan selesai. Urutan itu yang
kamu inginkan.
Peringatan Alpine
Dokumentasi RoadRunner menyatakannya secara eksplisit: hindari image berbasis Alpine. Alpine memakai musl libc, yang tidak sepenuhnya kompatibel dengan biner Go RoadRunner dan banyak ekstensi PHP โ dan hasilnya bisa berupa ketidakstabilan runtime serta masalah produksi yang sulit didiagnosis. Pakai Debian, Ubuntu, atau image PHP resmi non-Alpine.
Perhatikan bahwa FrankenPHP memberi saran yang sama dengan alasan berbeda. Kalau kamu terbiasa memilih Alpine demi ukuran image, kedua runtime ini sama-sama meminta kamu berhenti melakukannya.
Menjalankan worker sebagai user lain
server:
command: "php ./vendor/bin/roadrunner-worker"
user: "www-data" # RoadRunner harus dijalankan sebagai root untuk ini
Berguna di server tradisional. Di kontainer, pola yang lebih lazim adalah menjalankan seluruh kontainer sebagai pengguna tanpa hak istimewa sejak awal โ dan itulah yang kita lakukan di Dockerfile Fase 9.
Daftar periksa
- Image berbasis Debian atau Ubuntu, bukan Alpine.
opcache.enable_cli=1aktif.memory_limitdi bawahmax_worker_memory.exec_ttldisetel supaya permintaan menggantung tidak menahan worker.- RPC tidak terekspos ke luar.
- Health check terpasang, keep-alive aktif di lapisan depan.
- Log dalam format JSON ke
stdout. - Jumlah worker dihitung dari memori kontainer, bukan dari jumlah inti host.
Latihan: jalankan uji beban dengan keep-alive aktif lalu dimatikan
(ab -k versus ab), dan bandingkan throughput-nya. Angka yang kamu dapat adalah versi
nyata dari klaim 40% di dokumentasi โ untuk aplikasimu sendiri.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.