Memproses 200 Juta Request per Hari dengan CGI-bin
(simonwillison.net)- Jake Gold menjalankan program CGI Go + SQLite pada AMD 3700X 16 thread untuk melihat sejauh mana CGI ala 1990-an dapat bertahan di hardware modern
- Hasilnya menunjukkan bahwa bahkan dengan hardware biasa, CGI mampu memproses lebih dari 2.400 request per detik, atau lebih dari 200 juta request per hari
- Karena CGI memulai, menjalankan, lalu mengakhiri proses untuk setiap request, overhead-nya dulu besar; untuk menguranginya, pendekatan seperti PHP dan FastCGI muncul
- Pada 2020, Simon Willison menggunakan datasette-ripgrep untuk memproses pencarian dengan memanggil CLI ripgrep berbasis Rust, dan pengalamannya mengubah keyakinan lama bahwa menjalankan proses saat request web harus dihindari
- Dengan bahasa yang cepat saat startup seperti Go dan Rust serta lingkungan multi-CPU, pemrosesan ala CGI bisa menjadi opsi yang lebih realistis dibanding dulu, meski bukan pendekatan yang umumnya direkomendasikan
Meninjau Ulang Performa CGI di Hardware Modern
- Serving 200 million requests per day with a cgi-bin adalah tulisan Jake Gold yang menguji performa CGI ala 1990-an dengan program CGI Go + SQLite
- Lingkungan pengujiannya adalah sistem berbasis AMD 3700X 16 thread
- Hasil utamanya: bahkan dengan CGI, hardware biasa dapat memproses lebih dari 2.400 request per detik, atau lebih dari 200 juta request per hari
- CGI bekerja dengan memulai proses terpisah untuk setiap request masuk, menjalankannya, lalu mengakhirinya
- Untuk menghindari overhead ini, komunitas web awal membuat PHP dan FastCGI, yang menempatkan kode tetap di memori agar biaya tambahan berkurang
Mengapa CGI Mungkin Tidak Seburuk Dulu
- Salah satu bottleneck CGI di masa lalu adalah skrip web ditulis dengan bahasa seperti Perl, Python, Java yang tidak dirancang dengan tujuan utama memiliki startup yang sangat cepat
- Saat ini, dengan Go dan Rust, pemrosesan request bergaya CGI dapat berjalan jauh lebih efektif
- Pada 2020, saat membuat datasette-ripgrep, Simon Willison menjalankan pencarian dengan memanggil tool CLI ripgrep yang ditulis dalam Rust melalui shell, dan memperoleh hasil yang baik
- Karena program CGI berjalan sebagai proses terpisah, strukturnya juga dapat cocok untuk memanfaatkan banyak CPU
- Server modern dapat memiliki 384 thread CPU
- VM kecil pun dapat memiliki 16 CPU
- Performa CPU dan memori juga jauh lebih cepat dibanding masa lalu
- Cara menulis aplikasi web ala 1998 pun menjadi eksperimen yang menarik ketika bertemu Go dan Rust, tetapi bukan pilihan default yang sebaiknya diikuti sebagian besar layanan
1 komentar
Komentar Hacker News
Bahkan jika memakai sesuatu seperti Python, CGI saat ini cukup cepat
Jika memulai skrip CGI memakan CPU 400ms dan server punya 64 core, itu berarti 160 request per detik, atau 14 juta hit per hari per server
Jika sebuah layanan web kesulitan menangani beberapa juta request per hari di luar aset statis, bottleneck-nya bukan waktu startup proses CGI
Dulu saya mungkin akan menyebutnya “teknologi membosankan yang sudah lama didukung di pustaka standar Python”, tetapi para maintainer Python yang tersisa tampaknya menganggap stabilitas dan kompatibilitas mundur sebagai sesuatu yang merugikan, dan terus menghapus modul yang terlalu membosankan dan stabil dari pustaka standar
Modul cgi dihapus pada Python 3.13
Saya sudah memakainya hampir setiap hari selama sebagian besar 25 tahun terakhir, jadi masih punya kebiasaan memakai Python untuk prototyping, tetapi sekarang rasanya disayangkan dan saya sedang bimbang antara JS dan Lua
Menariknya, di sana ditautkan https://peps.python.org/pep-0206/ dari 14 Juli 2000, yaitu 25 tahun lalu, dan bahkan saat itu paket cgi sudah digambarkan sebagai “dirancang dengan buruk dan sekarang hampir mustahil diperbaiki”
Paket https://github.com/jackrosenthal/legacy-cgi tampaknya menyediakan pengganti drop-in untuk modul pustaka standar tersebut
Dukungan itu masih ada di CGIHTTPRequestHandler dalam modul http.server
Yang ada di modul cgi hanyalah beberapa fungsi untuk mem-parsing data formulir HTML
Tetapi saya kurang paham kalau alternatif berikutnya yang dipertimbangkan justru JS, yang bahkan tidak punya pustaka standar itu sendiri
Lua juga tidak punya modul CGI di pustaka standarnya
Sekarang lebih mirip mencari bagian yang benar-benar boros dan tidak masuk akal yang dimasukkan demi cepat rilis, lalu menghentikannya
Jika dikerjakan dengan benar, aplikasi bisa jadi hampir tidak menambah latensi selain akses ke penyimpanan
Saya belajar Python sejak 1.6, tetapi selama ini lebih banyak memakainya untuk scripting sistem operasi
Pada 1999~2003 saya memakai Tcl di modul Apache dan IIS, lalu terus menulis ulang modulnya dalam C, dan terlalu banyak pelajaran yang saya pelajari dengan susah payah
Baru-baru ini saya menguji binary Golang, RabbitMQ, Redis, dan MySQL semuanya di mesin mini server seharga 350 dolar, dan itu mampu menangani 5.000 request per detik secara berkelanjutan
Kalau dihitung 24 jam sehari, itu berarti 400 juta request
Luar biasa melihat betapa bagusnya alat-alat gratis saat ini, dan sekaligus luar biasa bahwa kita tetap membayar penyedia cloud begitu banyak uang
Ini memang bukan perbandingan yang benar-benar setara, tetapi saya sangat menikmati proses mengembangkan dan men-tuning semuanya sendiri di perangkat yang ada di ruang bawah tanah
Aneh bahwa orang terus menerima overhead seperti ini tanpa alasan selain “karena Google juga melakukannya”
Saya benar-benar harus menulis tentang arsitektur monolit modular yang kami pakai dengan baik
Anda bisa menyewa server dedicated dari perusahaan hosting dan memakainya sesuka hati, hanya saja biasanya sangat dibatasi oleh bandwidth atau kuota transfer
Saya paham kenapa cloud dipakai: ada banyak kepentingan di situ. Misalnya VC dan investor juga punya saham di perusahaan cloud, dan mereka takut investasi gagal gara-gara lonjakan trafik imajiner yang sebenarnya tidak akan pernah datang
Tim penjualan cloud sangat piawai memainkan kecemasan investor
Kalau drive rusak saat saya sedang bepergian, habislah sudah, dan kalau saya tidak sengaja menguncinya, saya bahkan tidak punya terminal serial cadangan untuk fallback
Saya bisa saja menyiapkannya seperti orang-orang di r/homelab, tetapi kalau begitu juga belum jelas apakah benar-benar menghemat uang, terutama kalau waktu saya sendiri ikut dihitung
Pada akhirnya saya melihat penyedia cloud sebagai transaksi yang cukup bagus berkat skala ekonominya
Anehnya, saya tidak bisa mendapatkan itu saja secara terpisah
Bayangkan seberapa besar bisnis atau layanan yang bisa dijalankan hanya dengan 4 drive 20TB dan CPU yang lumayan
Tidak mudah mendapatkan konfigurasi seperti itu dari penyedia cloud
Bagian yang mengatakan “komunitas web awal cepat belajar bahwa ini ide buruk lalu menemukan teknologi seperti PHP” secara teknis inti teknologinya adalah mod_php
PHP sendiri tidak berbeda dari Perl dalam cara eksekusinya, tetapi berkat pilihan desain mod_php dibanding mod_perl, skrip PHP bisa berjalan cepat hanya dengan dilempar begitu saja ke server, sedangkan mod_perl butuh sedikit pemikiran dan sihir agar bisa bekerja
Ada manajemen konten, unggah tugas, kalender acara, manajemen nilai, chat real-time, forum, dan semuanya dibuat dalam C murni di atas CGI, dan rasanya seperti neraka untuk dikerjakan
Hari ketika saya menemukan PHP saya hampir menangis, karena semua hal yang sebelumnya harus saya bangun dari nol dengan susah payah sambil membaca RFC atau merekayasa balik HTTP, di PHP ternyata cuma berupa pemanggilan fungsi sederhana
Saya tidak perlu lagi men-debug implementasi urlencode yang amburadul atau kehilangan satu hari hanya karena satu carriage return aneh di header HTTP
Sepertinya versi pertama modul Apache itu adalah untuk PHP/FI Version 2.0 pada 1996: https://www.php.net/manual/phpfi2.php#module
Jika
cgi-binperlu mengakses DB, koneksi harus dibuka setiap kali proses dimulaiMenyimpan kode tetap berada di memori seperti FastCGI bukan hanya untuk menghindari biaya waktu startup, tetapi juga karena bisa memiliki pool koneksi DB atau setidaknya koneksi persisten per thread
Hal itu terjadi ketika “Python itu single-threaded, jadi mari jalankan banyak instance” dan “Python itu lambat, jadi mari jalankan banyak instance” dilakukan bersamaan
Pada skala besar, pada akhirnya harus memakai pool koneksi bersama di luar Python seperti pgbouncer dan banyak tuning agar bisa menangani beban tanpa membunuh DB
Tentu saja setelah itu, ketika diimplementasikan ulang dalam bahasa multithread dengan performa yang lumayan, semuanya kembali menjadi sangat sederhana
Pada dasarnya dengan menjalankan daemon terpisah yang berperan seperti proxy
Jika memakai socket Unix alih-alih TCP/IP, biaya koneksi relatif lebih rendah
Jika di hardware ini aplikasi hello world hanya mampu 2.400 request per detik, bukankah itu agak buruk
Saya juga tidak paham, dengan mengorbankan performa, tepatnya apa yang didapat. Kodenya juga tidak jadi lebih sederhana
Situs seperti itu hanya sebagian kecil dari keseluruhan
Sepertinya bahkan bisa menahan ledakan trafik HN
Ini juga sudah dibahas kemarin: https://news.ycombinator.com/item?id=44464272
Di perusahaan, kami kadang masih menyajikan direktori cgi-bin untuk aplikasi web internal yang perlu dibuat cepat
Menjaganya tetap sederhana membuatnya nyaman digunakan
CGI tidak berarti Anda harus langsung
printHTTP/1.0 ke stdoutMisalnya, dengan Python bawaan
wsgiref.handlers.CGIHandler, aplikasi WSGI apa pun bisa dijalankan sebagai skrip CGIimport wsgiref.handlers, flask
app = flask.Flask(name)
wsgiref.handlers.CGIHandler().run(app)
Kami menjalankan skrip dengan uwsgi dan plugin CGI-nya[1]
Rasanya lebih sederhana dan fleksibel daripada menjalankan Apache atau lighttpd hanya karena mod_cgi
Karena uwsgi dijalankan sebagai unit systemd, kami juga bisa memanfaatkan penuh fitur hardening dan sandboxing dari systemd
Salah satu hal praktis pada penanganan CGI di uwsgi yang tidak ada di mod_cgi adalah Anda bisa menentukan interpreter untuk format file tertentu
cgi = /cgi-bin=/webapps/cgi-bin/src
cgi-allowed-ext = .py
cgi-helper = .py=/webapps/cgi-bin/venv/bin/python3 # all dependencies go here
Waktu sampai byte pertama adalah 250~350ms, dan itu bisa diterima untuk kebutuhan kami
[1]: https://uwsgi-docs.readthedocs.io/en/latest/CGI.html
wsgiref.handlers.CGIHandlertampaknya masih belum dijadwalkan untuk deprecatedSaya baru-baru ini memakai Apache untuk proyek sampingan, dan salah satu alasannya adalah fitur .htaccess, jadi kami sempat membahas hal serupa
Anda bisa meletakkan file
.htaccessdi mana saja dan Apache akan membacanya sebagai konfigurasi server tambahan pada setiap request: https://httpd.apache.org/docs/2.4/howto/htaccess.htmlAlasan besar dulu untuk menghindarinya adalah performa. Setiap request memerlukan akses disk tambahan, dan kalau memungkinkan selalu lebih baik menaruhnya di file konfigurasi utama
Tetapi sekarang sebagian besar server memakai SSD, dan kemungkinan Linux juga punya RAM luang untuk dipakai sebagai cache file system
Tentu saja performanya masih sedikit lebih buruk karena Apache harus me-parse konfigurasi pada setiap request, bukan hanya sekali
Meski begitu, melihat CPU server modern yang jauh lebih kuat, untuk banyak kasus penggunaan hal itu masih bisa ditoleransi
Proyek sampingan itu masih sangat awal, tetapi sudah dipakai: https://github.com/StaticPatch/StaticPatch/tree/main
“Saya bukan programmer sungguhan. Saya hanya mencoba-coba sampai berhasil lalu lanjut. Programmer sungguhan akan berkata, ‘Ya, ini memang jalan, tapi ada kebocoran memori di mana-mana. Mungkin harus diperbaiki?’ Saya cukup me-restart Apache setiap 10 request.”
Sejak itu PHP sudah berkembang sangat jauh, tetapi sebagian besar perjalanan itu adalah proses memperbaiki kesalahan awal
“Alasan PHP 8 jauh lebih baik adalah karena kode saya di dalamnya jauh lebih sedikit.”
Karena pilihan ini, 99,99% request HTTP jadi melambat akibat pembacaan disk yang sebenarnya tidak perlu
Saya jadi lebih sering memikirkan ini sebagai bagian dari alur kerja pembuatan prototipe cepat
Bahasa JIT modern tampaknya waktu start-up-nya akan sebagian besar didominasi oleh import, kecuali jika memakai model FastCGI
Saya mulai memikirkan ini setelah mulai memakai web server h2o untuk skrip lokal; dengan mruby dan handler FastCGI, file konfigurasi bisa ditulis dengan rapi dan cepat, dan kinerjanya juga sangat cepat: https://h2o.examp1e.net/configure/fastcgi_directives.html
Kegunaan lain yang bermanfaat adalah ketika pelanggan memperluas perangkat lunak lokal dengan kode mereka sendiri
Misalnya, alih-alih menyuruh mereka memakai MCP untuk memperluas alat AI, kita bisa meminta mereka mengimplementasikan struktur permintaan tertentu dengan CGI
Saya jadi penasaran apakah layanan MCP itu sendiri juga bisa diimplementasikan dengan CGI
Framework MCP mungkin juga bisa mengekspos fungsionalitas dalam bentuk program yang mendukung kedua mode eksekusi
Saya perlu menggali spesifikasinya lebih jauh
Bagi saya,
inetditulah CGIItu membuat internet jauh lebih menyenangkan
Saya sendiri pernah meng-host berbagai skrip shell lewat inetd, dan bahkan punya server HTTP yang sepenuhnya ditulis dalam Bash
VPS itu sudah lama hilang, dan waktu itu saya juga belum memakai version control
Laptop tempat saya menulisnya pun sudah tidak ada
Tapi tetap saja, itu sangat menyenangkan
Deploy semudah Makefile + scp, dan pengujiannya adalah skrip Bash lain yang mencampurkan beberapa
netcatdan grepBenar-benar masa yang indah