Mempelajari plugin HTTP RoadRunner secara menyeluruh: konfigurasi http.address dan middleware bawaan seperti gzip dan static, pengaturan pool num_workers, max_worker_memory, max_jobs, hingga praktik melayani static files bersama dynamic routes.

Setelah di episode 4 kita membuat worker PSR-7 yang bisa merespons request, pada episode kali ini kita membesarkan skala: mengkonfigurasi HTTP plugin secara penuh — alamat listen, middleware bawaan, dan parameter pool — lalu melayani kombinasi static files dan dynamic routes seperti aplikasi produksi sungguhan.
Mengapa episode ini penting? Karena di sinilah kalian mulai membuat keputusan operasional: berapa worker, berapa memory cap, apa yang dilayani Go langsung dan apa yang diteruskan ke PHP. Keputusan ini menentukan latensi dan penggunaan resource — dan hampir semua aplikasi RoadRunner produksi memulai dari blok http seperti yang kita bahas sekarang.
httphttp:
address: 0.0.0.0:8080
max_request_size: 1024
middleware: ["gzip", "static", "headers", "sendfile"]
uploads:
dir: "/tmp/uploads"
forbid: [".php", ".exe", ".bat"]
pool:
num_workers: 8
max_worker_memory: 128
max_jobs: 1000
allocate_workers_by_tasks: true
static:
dir: public
forbid: [".php", ".htaccess"]
cache:
etag: true
last_modified: true| Key | Fungsi |
|---|---|
address | Alamat listen TCP (host:port) |
max_request_size | Limit ukuran request (KB) untuk melindungi dari body raksasa |
middleware | Daftar middleware HTTP bawaan yang aktif, urutannya penting |
uploads | Konfigurasi file upload: direktori simpan dan daftar ekstensi terlarang |
pool | Parameter worker pool untuk aplikasi HTTP |
static | Melayani file statis langsung dari Go, tanpa menyentuh PHP |
Note
Order middleware menentukan urutan eksekusi. Aturan praktis: taruh static di posisi awal agar file statis direspons Go tanpa mencapai worker PHP, dan taruh transformasi response seperti gzip setelahnya.
RoadRunner menyediakan middleware HTTP yang diaktifkan hanya dengan menulis namanya di daftar middleware:
| Middleware | Fungsi |
|---|---|
gzip | Kompres response (dan request) dengan gzip/zstd/brotli |
static | Melayani file dari http.static.dir tanpa melibatkan PHP |
sendfile | Mengirim file besar lewat kernel (sendfile(2)) — hemat memory Go |
headers | Menambahkan header ke semua response |
request | Instrumen request untuk metrics (biasanya bersama plugin metrics) |
http_metrics | Metric HTTP otomatis (endpoint /metrics, episode 12) |
Contoh penggunaan headers dan sendfile:
http:
address: 0.0.0.0:8080
middleware: ["static", "gzip", "headers", "sendfile"]
headers:
response:
"X-Powered-By": "RoadRunner"
"X-Frame-Options": "DENY"Parameter pool menentukan bagaimana worker di-spawn dan dirawat:
pool:
num_workers: 8
max_worker_memory: 128
max_jobs: 1000
allocate_workers_by_tasks: true
supervisor:
max_worker_memory: 128
exec_ttl: 0
idle_ttl: 60
ttl: 0Penjelasan parameter inti:
num_workers — jumlah worker yang hidup di pool. Mulai dari jumlah core CPU dan sesuaikan dari hasil benchmark (episode 20).max_jobs — worker di-restart setelah melayani N request. Ini pengaman utama terhadap memory leak bertahap di PHP.max_worker_memory — worker di-restart bila penggunaan memory (RSS) melebihi nilai ini (MB).allocate_workers_by_tasks — bila true, worker di-spawn bertahap sesuai beban, bukan semua sekaligus. Berguna untuk hemat resource saat idle.supervisor menambahkan aturan restart berbasis waktu: idle_ttl (detik idle sebelum direstart), exec_ttl (durasi maksimum satu task), dan ttl (umur maksimum worker). Kombinasi ini menjaga pool tetap segar tanpa downtime manual.
Mari bangun aplikasi kecil yang melayani static assets dari Go dan dynamic routes dari PHP. Susun struktur proyek:
rr-lab/
app.php # worker PSR-7 (episode 4)
.rr.yaml # config dengan static.dir
public/
index.html # halaman statis
assets/
app.cssUpdate bagian static dan middleware di .rr.yaml, lalu tulis route dinamis di worker — tambahkan pengecekan path sebelum merespons:
while ($request = $psr7->waitRequest()) {
try {
$path = $request->getUri()->getPath();
$status = 200;
$body = 'Not Found';
if ($path === '/' || $path === '/home') {
$body = '<h1>Home</h1><a href="/assets/app.css">CSS</a>';
} elseif ($path === '/api/health') {
$body = json_encode(['status' => 'ok', 'pid' => getmypid()]);
}
$response = new Response($status, ['Content-Type' => 'text/html; charset=utf-8'], $body);
$psr7->respond($response);
} catch (\Throwable $e) {
$psr7->getWorker()->error((string) $e);
}
}Restart server dan uji:
./rr serve -c .rr.yaml &
curl http://localhost:8080/assets/app.css # dilayani Go, tidak menyentuh PHP
curl http://localhost:8080/api/health # dilayani worker PHPBandingkan latensi keduanya: respons statis datang dari Go dalam hitungan mikro detik dan tidak memakai worker — inilah mengapa static selalu disarankan untuk aset, sementara PHP hanya untuk konten dinamis.
Warning
Perhatikan static.forbid — daftar ekstensi yang tidak boleh dilayani sebagai file statis (misal .php). Tanpa ini, jika ada file .php di dalam public/, Go bisa membocorkan source code-nya. Selalu sediakan forbid: [".php", ".htaccess"].
Pada episode 5 ini, kalian telah menguasai HTTP plugin dan pool management.
Inti yang harus dibawa pulang:
http mengatur alamat, ukuran request, upload, pool, dan static.middleware — order menentukan eksekusi.num_workers, max_jobs, max_worker_memory, dan supervisor.static.forbid untuk keamanan.Di episode 6 selanjutnya, kita mengintegrasikan framework: menjalankan Laravel via Octane dan Symfony via RoadRunner Runtime, termasuk konfigurasi worker count dan warm-up — sehingga kalian tidak perlu menulis worker manual lagi. Sampai jumpa di episode 6!