Perbandingan Karakteristik Runtime: PHP-FPM vs FrankenPHP
| Parameter Evaluasi | Nginx + PHP-FPM Tradisional | FrankenPHP Worker Mode |
|---|---|---|
| Model Komunikasi | FastCGI via Unix Socket / TCP Loopback | In-Process Memory Call (CGO libphp) |
| Siklus Hidup Framework | Bootstrapped per setiap HTTP request | Bootstrapped satu kali saat inisialisasi worker |
| Overhead Cold-Start | 15ms - 45ms per request (Framework berat) | 0ms (Tersedia langsung di RAM) |
| Konsumsi CPU Baseline | Tinggi (Kompilasi opcode / autoloader loop) | Rendah (Hanya pemrosesan business logic) |
| Risiko State Contamination | Nol (Proses dibersihkan total per request) | Tinggi (Objek statis & singleton tetap hidup) |
| Manajemen Memori | Otomatis dibebaskan saat request termination | Manual / Bergantung pada Garbage Collection & cycle restart |
| Dukungan Protokol Edge | Bergantung pada Nginx (HTTP/1.1, HTTP/2) | Caddy Native (HTTP/1.1, HTTP/2, HTTP/3 QUIC) |
TEXT
+-----------------------------------------------------------------------------------+
| ALIRAN REQUEST RUNTIME: PHP-FPM VS FRANKENPHP WORKER |
+-----------------------------------------------------------------------------------+
[Model PHP-FPM Tradisional]
Client Request ---> Nginx ---> FastCGI Protocol ---> PHP-FPM Pool Worker
|--> Bootstrap Framework
|--> Eksekusi Logika
|--> Teardown & Free Memory (Die)
[Model FrankenPHP Worker Mode]
Client Request ---> Caddy Engine (Go) ---> Direct CGO Call ---> Persistent Worker Thread
|--> (Framework Sudah Ada di RAM)
|--> Reset Request State
|--> Eksekusi Logika
|--> Flush Output (Worker Tetap Hidup)Audit dan Verifikasi Isolasi State Sebelum Migrasi
1. Deteksi Variabel Statis dan Memory Bloat
- Singleton Service Container: Jangan menyimpan konteks request (seperti instance user terautentikasi atau input data) di dalam properti singleton.
- Unbuffered Database Cursors: Pastikan koneksi database tidak meninggalkan transaksi menggantung (uncommitted transactions) jika request gagal di tengah jalan.
- Penyimpanan Log Lokal In-Memory: Array statis yang mengakumulasi string error atau query log di dalam memori tanpa batas maksimum.
2. Skrip Worker Wrapper Standar Produksi
frankenphp_handle_request():
PHP
<?php
// public/frankenphp-worker.php
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
ignore_user_abort(true);
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
// Handler loop yang dieksekusi terus-menerus oleh FrankenPHP engine
$handler = static function () use ($kernel): void {
$request = Request::capture();
// Eksekusi request melalui kernel framework
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
// Pembersihan state konteks request secara eksplisit
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
};
// Menjalankan persistent worker loop dengan kuota restart request
$maxRequests = (int)($_ENV['MAX_REQUESTS'] ?? 1000);
for ($nbRequests = 0; $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
if (!$keepRunning) {
break;
}
}
// Worker keluar secara natural untuk mencegah memory accumulation
exit(0);Konfigurasi Produksi FrankenPHP (Caddyfile)
/etc/frankenphp/Caddyfile. Konfigurasi ini mengatur isolasi worker pool, batas alokasi request, serta integrasi logging performa tinggi:
CADDYFILE
{
# Matikan admin endpoint jika tidak digunakan di production
admin off
# Konfigurasi global FrankenPHP engine
frankenphp {
# Tentukan skrip entrypoint worker dan jumlah thread worker
worker /var/www/html/public/frankenphp-worker.php {
num 16
env MAX_REQUESTS 1500
}
}
}
:8080 {
root * /var/www/html/public
encode zstd gzip
# Header keamanan dasar
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
}
# Lewatkan request ke PHP runtime
php_server {
resolve_root_symlink
}
# Nonaktifkan akses langsung ke hidden files
@hidden {
path /.* */.* /vendor/*
}
error @hidden 404
log {
output file /var/log/frankenphp/access.log {
roll_size 50mb
roll_keep 10
}
format json
}
}/etc/systemd/system/frankenphp.service:
INI
[Unit]
Description=FrankenPHP High-Performance Server Engine
After=network.target network-online.target
Wants=network-online.target
[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/var/www/html
ExecStart=/usr/local/bin/frankenphp run --config /etc/frankenphp/Caddyfile
ExecReload=/usr/local/bin/frankenphp reload --config /etc/frankenphp/Caddyfile
TimeoutStopSec=15s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
Restart=always
RestartSec=2s
# Batasan memori cgroups untuk mencegah OOM Killer mematikan seluruh instance
MemoryHigh=3500M
MemoryMax=4000M
[Install]
WantedBy=multi-user.targetProsedur Cutover Bertahap (Canary Traffic Shift)
TEXT
[Load Balancer / Reverse Proxy]
|
+-------------------+-------------------+
| (Bobot 90%) | (Bobot 10% -> 50% -> 100%)
v v
[Klaster PHP-FPM] [Klaster FrankenPHP]
(Port 9000 Unix) (Worker Mode :8080)Tahap 1: Persiapan dan Pemeriksaan Sintaks
BASH
# Verifikasi versi dan ekstensi FrankenPHP
frankenphp php-cli -v
frankenphp php-cli -m | sort > /tmp/frankenphp_modules.txt
php-fpm8.3 -m | sort > /tmp/phpfpm_modules.txt
# Periksa apakah ada ekstensi yang hilang
diff -u /tmp/phpfpm_modules.txt /tmp/frankenphp_modules.txt
# Validasi konfigurasi Caddyfile
frankenphp validate --config /etc/frankenphp/CaddyfileTahap 2: Uji Validasi State dan Internal Smoke Test
curl dengan mengirimkan request secara berurutan pada worker yang sama, memastikan data session tidak tertukar:
BASH
# Kirimkan 50 request beruntun untuk mengecek kebocoran konteks session
for i in {1..50}; do
TOKEN=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/api/v1/auth-check \
-H 'Authorization: Bearer test-token-user-alpha')
if [ "$TOKEN" != "200" ]; then
echo "Peringatan: Response code menyimpang pada iterasi $i: $TOKEN"
exit 1
fi
done
echo "Smoke test berhasil tanpa anomali response."Tahap 3: Pergeseran Bobot Trafik (Canary Routing)
BASH
# Contoh manipulasi bobot upstream via HAProxy Runtime API
echo 'set weight backend_app/frankenphp_node1 5' | socat stdio /run/haproxy/admin.sock
echo 'set weight backend_app/phpfpm_node1 95' | socat stdio /run/haproxy/admin.sock- 5% selama 30 menit.
- 25% selama 1 jam.
- 50% selama 2 jam.
- 100% (Cutover tuntas, PHP-FPM diatur ke mode standby).
Penanganan Memory Drift dan Operasional Worker
1. Rotasi Berkala Berdasarkan Hitungan Request
MAX_REQUESTS pada skrip wrapper worker (misalnya 1.000 atau 2.000 request), proses worker akan melakukan exit(0) secara anggun (graceful exit). Engine FrankenPHP di Golang secara otomatis akan melahirkan (spawn) worker thread baru untuk menggantikannya tanpa menyebabkan antrean request terputus.
2. Skrip Pemantauan Memory Footprint per Worker
BASH
#!/usr/bin/env bash
# /usr/local/bin/check-worker-memory.sh
set -euo pipefail
PID=$(pgrep -f 'frankenphp run' || true)
if [ -z "$PID" ]; then
echo "CRITICAL: FrankenPHP service tidak berjalan!"
exit 2
fi
# Ambil RSS memory dalam Megabytes
RSS_MEM_KB=$(ps -p "$PID" -o rss=)
RSS_MEM_MB=$((RSS_MEM_KB / 1024))
THRESHOLD_WARN=2500
THRESHOLD_CRIT=3500
echo "FrankenPHP PID $PID menggunakan ${RSS_MEM_MB} MB RAM."
if [ "$RSS_MEM_MB" -ge "$THRESHOLD_CRIT" ]; then
echo "CRITICAL: Penggunaan memori melampaui batas kritis (${THRESHOLD_CRIT} MB)!"
# Kirim sinyal graceful reload ke FrankenPHP
systemctl reload frankenphp
exit 2
elif [ "$RSS_MEM_MB" -ge "$THRESHOLD_WARN" ]; then
echo "WARNING: Penggunaan memori mendekati batas ambang (${THRESHOLD_WARN} MB)."
exit 1
else
echo "OK: Alokasi memori berada dalam rentang normal."
exit 0
fiCRON
*/5 * * * * root /usr/local/bin/check-worker-memory.sh > /dev/null 2>&1Rencana Pemulihan Instan (Rollback Runbook)
- Kembalikan Proxy Traffic ke PHP-FPM:
BASH
echo 'set weight backend_app/frankenphp_node1 0' | socat stdio /run/haproxy/admin.sock echo 'set weight backend_app/phpfpm_node1 100' | socat stdio /run/haproxy/admin.sock - Hentikan Layanan FrankenPHP untuk Membebaskan Sumber Daya:
BASH
systemctl stop frankenphp - Evaluasi Core Dump dan Log Error:
Periksa
/var/log/frankenphp/access.logdan log sistemjournalctl -u frankenphp -n 200 --no-pageruntuk mengidentifikasi skrip PHP yang memicu kegagalan siklus worker.
