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

FrankenPHP worker mode

Octane menyembunyikan detail ini darimu, tapi memahaminya membuat perilaku aneh di produksi jadi masuk akal — terutama soal apa yang direset dan apa yang tidak.

Sumber asli frankenphp.dev Resmi Rangkuman ~7 menit baca

Intisari

  • Skrip worker mem-bootstrap aplikasi sekali, lalu memanggil frankenphp_handle_request() berulang.
  • Superglobal $_GET, $_POST, $_SERVER direset tiap permintaan — tapi $_ENV tidak.
  • Variabel statis, properti statis kelas, dan variabel global bertahan. Itulah sumber kecepatannya.
  • Worker yang keluar dengan kode ≠ 0 di-restart dengan jeda menaik; terlalu sering gagal = server berhenti.
  • Worker bisa dimuat ulang lewat API admin Caddy tanpa menjatuhkan lalu lintas.

Bentuk sebuah skrip worker

<?php
// public/worker.php — inilah yang dilakukan Octane untukmu

require __DIR__.'/../vendor/autoload.php';

$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();        // ← SEKALI SAJA

$handler = static function () use ($app) {
    try {
        // Dipanggil saat permintaan masuk. Superglobal sudah direset di sini.
        $request  = Request::capture();
        $response = $app->handle($request);
        $response->send();
        $app->terminate($request, $response);
    } catch (Throwable $e) {
        // set_exception_handler hanya dipanggil saat skrip worker BERAKHIR,
        // jadi exception wajib ditangkap di sini.
        report($e);
        http_response_code(500);
    }
};

$maks = (int) ($_SERVER['MAX_REQUESTS'] ?? 0);

for ($n = 0; ! $maks || $n < $maks; ++$n) {
    $lanjut = frankenphp_handle_request($handler);

    gc_collect_cycles();      // kurangi peluang GC berjalan di tengah render halaman

    if (! $lanjut) {
        break;
    }
}

Perhatikan blok try/catch di dalam handler. Di PHP biasa, set_exception_handler() menangkap exception yang lolos. Di worker mode, handler itu baru terpanggil saat seluruh skrip worker berakhir — bukan saat sebuah permintaan gagal. Exception yang tidak ditangkap di dalam handler akan mematikan worker, bukan sekadar menggagalkan satu permintaan.

Perilaku superglobal

SuperglobalDireset tiap permintaan?
$_GET, $_POST, $_COOKIEYa
$_FILES, $_REQUESTYa
$_SERVERYa
$_ENVTidak

$_ENV tidak direset — dan itu punya konsekuensi keamanan. Apa pun yang kamu tulis ke $_ENV selama sebuah permintaan akan tetap terlihat oleh permintaan berikutnya di utas yang sama. Jangan pernah menaruh data khusus permintaan atau data sensitif di sana. Untuk konfigurasi, tetap lewat config().

Sebelum panggilan pertama frankenphp_handle_request(), superglobal berisi nilai milik skrip worker itu sendiri. Kalau kamu butuh nilai tersebut di dalam handler, salin dulu ke variabel dan bawa lewat use — setelah loop dimulai, nilainya sudah tergantikan oleh nilai permintaan.

State apa yang bertahan

function penghitung(): int
{
    static $n = 0;

    return ++$n;      // 1, 2, 3, … untuk setiap permintaan di utas ini
}
Bertahan antar permintaanContoh
Variabel statis di fungsi/methodstatic $n = 0;
Properti statis kelasMetrik::$peristiwa
Variabel global skrip worker$app
Apa pun di memori di luar handlerArray cache, koneksi

Ini bukan kebetulan atau cacat — inilah yang membuat worker mode cepat. Framework seperti Laravel Octane sudah mereset state milik framework; yang tersisa adalah state milik kodemu sendiri, dan itulah isi materi jebakan state sebelumnya.

Jumlah worker dan utas

{
	frankenphp {
		num_threads 16
		worker {
			file /app/public/worker.php
			num 8
		}
	}
}

Secara default FrankenPHP menyalakan utas dan worker sebanyak dua kali jumlah inti CPU. Dokumentasinya secara eksplisit menyarankan mengubah nilai ini, dengan satu aturan yang layak dihafal:

num_threads × memory_limit < memori yang tersedia

Aturan itu menyelamatkanmu dari OOM kill. Dengan memory_limit=256M dan 32 utas, kamu memesan sampai 8 GB — di kontainer berjatah 2 GB, itu berarti proses dibunuh saat lalu lintas naik, bukan saat kamu mengujinya. Setel num_threads berdasarkan memori kontainer, bukan berdasarkan jumlah inti mesin host.

max_threads untuk lonjakan

{
	frankenphp {
		num_threads 8         # selalu ada
		max_threads 24        # boleh naik sampai sini saat dibutuhkan
	}
}

Nilai auto akan memperkirakan batas berdasarkan memory_limit, tapi dokumentasinya memperingatkan bahwa perkiraan itu bisa jauh terlalu rendah. Untuk beban produksi, tentukan angkanya sendiri berdasarkan uji beban.

Kegagalan dan pemuatan ulang

# Muat ulang seluruh worker dengan rapi, lewat API admin Caddy
curl -X POST http://localhost:2019/frankenphp/workers/restart
KejadianPerilaku FrankenPHP
Worker keluar dengan kode 0Dianggap normal, dijalankan ulang
Worker keluar dengan kode ≠ 0Restart dengan jeda menaik
Gagal terus dalam waktu singkatServer berhenti: too many consecutive failures
Berkas berubah (dengan --watch)Restart otomatis — hanya untuk pengembangan

Kegagalan beruntun yang menjatuhkan server sebenarnya perilaku yang baik. Sebuah typo pada kode bootstrap akan membuat worker mati seketika, berulang kali. Alih-alih berputar tanpa henti sambil menolak semua permintaan, FrankenPHP berhenti — sehingga orkestrator (ECS) melihat kontainernya tidak sehat dan menggagalkan deploy, bukan menerima lalu lintas dengan aplikasi yang rusak.

Latihan: tulis fungsi penghitung() di atas, panggil dari sebuah rute, jalankan Octane dengan --workers=1, lalu muat halaman itu sepuluh kali dan amati angkanya naik. Restart lewat API admin, dan lihat ia kembali ke satu.

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