Belajar RoadRunner - Worker & PSR-7 Handling
Episode 4 of 26

Belajar RoadRunner - Worker & PSR-7 Handling

Membuat worker PHP pertama dengan spiral/roadrunner-worker, memahami kelas Worker dan PSR7Worker, membangun loop request-response yang benar, dan menangani error agar worker tidak mati diam-diam di tengah request.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 3 kita menyiapkan .rr.yaml yang benar — dengan server.command: "php app.php" — pada episode kali ini kita menulis file app.php itu sendiri: worker PHP yang menjadi jantung aplikasi long-running. Di sinilah PHP "berubah wujud" dari skrip per-request menjadi proses persistent yang melayani ribuan request.

Mengapa episode ini penting? Karena worker adalah tempat hampir semua bug RoadRunner terjadi: loop yang salah, error yang tidak tertangkap, response yang tidak dikirim — semuanya membuat worker mati atau request menggantung. Memahami pola worker yang benar adalah keterampilan dasar yang dipakai seluruh episode berikutnya.

Paket spiral/roadrunner-worker

Worker PHP dibangun di atas package Composer spiral/roadrunner-worker. Package ini menyediakan abstraksi untuk berkomunikasi dengan server Go melalui Goridge, termasuk implementasi PSR-7 untuk HTTP:

Install package worker
composer require spiral/roadrunner-worker spiral/roadrunner-http

Catatan: spiral/roadrunner-http membawa implementasi PSR-7 worker (kelas PSR7Worker), sedangkan spiral/roadrunner-worker menyediakan Worker inti yang dipakai semua jenis worker (HTTP, jobs, gRPC).

Kelas Worker dan PSR7Worker

Ada dua lapisan yang perlu dipahami:

100%
  • Spiral\RoadRunner\Worker — lapisan paling dasar. Meng-handle komunikasi biner dengan server Go (menerima payload, mengirim payload, menangani heartbeat). Dipakai langsung oleh worker jobs (episode 11) dan gRPC (episode 16).
  • Spiral\RoadRunner\Http\PSR7Worker — lapisan di atas Worker yang menangani request HTTP sebagai objek PSR-7 (Psr\Http\Message\ServerRequestInterface dan ResponseInterface).

Kalian menulis logika bisnis berhadapan dengan objek PSR-7 — bukan raw bytes — sehingga kode kalian portabel ke framework mana pun yang juga memakai PSR-7.

Buat file app.php di root proyek lab:

app.php - worker PSR-7 pertama
<?php
 
declare(strict_types=1);
 
use GuzzleHttp\Psr7\Response;
use Nyholm\Psr7\Factory\Psr17Factory;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\Worker;
 
require __DIR__ . '/vendor/autoload.php';
 
$psr17 = new Psr17Factory();
$worker = Worker::create();
$psr7 = new PSR7Worker($worker, $psr17, $psr17, $psr17);
 
while ($request = $psr7->waitRequest()) {
    try {
        $body = sprintf('Halo dari worker %s!', getmypid());
        $psr7->respond(new Response(200, ['Content-Type' => 'text/plain'], $body));
    } catch (\Throwable $e) {
        $psr7->getWorker()->error((string) $e);
    }
}

Penjelasan baris per baris:

  • Baris 13-15: membangun Psr17Factory (untuk membuat request/response), Worker::create(), lalu membungkusnya dalam PSR7Worker.
  • Baris 17: loop utamawaitRequest() memblokir sampai request datang dari server Go. Inilah yang membuat worker persistent: loop tidak pernah berakhir.
  • Baris 19-23: proses request dan kirim response lewat respond().
  • Baris 24-26: error fatal ditangkap dan dikirim ke server via error() — worker tidak mati diam-diam.

Menjalankan Server

Sekarang jalankan server:

Jalankan RoadRunner
./rr serve -c .rr.yaml

Server mendengarkan di port 8080 dan meng-spawn 4 worker (sesuai config episode 3). Uji dengan curl:

Uji worker pertama
curl http://localhost:8080/

Output: Halo dari worker 12345! — perhatikan PID berbeda di tiap respons bila pool memilih worker yang berbeda. Untuk memastikan worker benar-benar persistent, bandingkan header respons: worker yang sama melayani request berurutan tanpa bootstrap ulang.

Tip

Untuk melihat berapa request yang dilayani satu worker sebelum direstart, atur pool.max_jobs kecil (misal 5) di .rr.yaml, lalu request berulang kali sambil mengamati PID berganti. Ini cara paling visual memahami siklus hidup worker dari episode 2.

Pola yang Wajib Dihindari

Worker persistent mengubah cara menulis kode PHP. Tiga kesalahan paling umum:

KesalahanDampakSolusi
Inisialisasi berat di dalam loopBootstrap ulang tiap request — borosBangun dependency di luar loop
Object/state yang bocor antar requestData request sebelumnya ikut terbawaReset state per-request (episode 14)
exit() / die() di dalam kodeMembunuh worker — request lain tersangkutLempar exception, biarkan di-catch loop

Poin kunci: apa pun yang bisa dipindah ke luar loop, pindahkan. Koneksi database, koneksi Redis, cache opcode, dan container DI sebaiknya dibangun sekali saat worker lahir, lalu dipakai ulang untuk semua request. Di episode 6 kita lihat bagaimana framework menangani ini secara otomatis.

Menangani Error dan Shutdown

Jika worker menerima exception fatal yang tidak tertangkap, PHP akan berhenti dan server Go mendeteksi worker mati lalu meng-spawn pengganti — request yang sedang diproses gagal. Karena itu:

  1. Selalu bungkus logika per-request dalam try/catch (seperti contoh di atas).
  2. Kirim error ke server dengan $psr7->getWorker()->error($msg) supaya tercatat di log RoadRunner.
  3. Untuk graceful shutdown saat server menerima sinyal, worker bisa memanggil Worker::stop() — pola ini berguna saat kalian harus flush buffer sebelum mati.

Penutup

Pada episode 4 ini, kalian telah membuat worker HTTP pertama yang benar-benar berjalan.

Inti yang harus dibawa pulang:

  • spiral/roadrunner-worker menyediakan Worker (biner) dan PSR7Worker (HTTP PSR-7).
  • Loop while ($request = $psr7->waitRequest()) adalah inti worker persistent.
  • Bangun dependency di luar loop, reset state per-request.
  • Tangkap \Throwable dan kirim error ke server agar worker tidak mati diam-diam.
  • Worker tidak boleh exit(); biarkan supervisor yang mengelola siklus hidupnya.

Di episode 5 selanjutnya, kita masuk lebih dalam ke HTTP plugin & pool management — mengatur http.address, middleware bawaan (gzip, static), dan parameter pool (num_workers, max_worker_memory, max_jobs) untuk aplikasi yang lebih realistis dengan static files dan dynamic routes. Sampai jumpa di episode 5!

Belajar RoadRunner - Worker & PSR-7 Handling | Belajar RoadRunner