- PVSM.RU - https://www.pvsm.ru -

В девяностых CGI сделал интернет интерактивным, но чуть не убил его. Это неудивительно, ведь каждый клик пользователя порождал новый тяжеловесный процесс на сервере. Под катом разберу, из-за чего такой подход оказался бомбой замедленного действия, и почему именно технология FastCGI спасла веб от инфраструктурного коллапса.
В начале 1993 года, когда веб перестал быть набором статических страничек, команда NCSA опубликовала в рассылке www-talk спецификацию для вызова исполняемых файлов из командной строки. Другие разработчики тепло приняли её, и с тех пор CGI (Common Gateway Interface, не путать с computer-generated imagery) стала стандартом для веб-серверов.
Эта спецификация интерфейса позволяла веб-серверу запускать скрипт при каждом HTTP-запросе и передавать ему входные данные (например, информацию из формы), а затем возвращать пользователю ответ. Грубо говоря, это был очень простой способ оживления веб-страниц, потому что CGI-скрипты можно было писать на Perl, C, Python и даже Bash. Пример скрипта оставил ниже:
#!/usr/bin/perl
print "Content-Type: text/htmlnn";
print "<html><body><h1>Hello from CGI!</h1></body></html>";
В свою очередь, веб-сервер передавал параметры запроса через переменные окружения, контент запроса — через STDIN, а скрипт выводил результат для сервера через STDOUT. Старожилы ещё, наверное, помнят папку cgi-bin на серверах в то время. В ней лежали заветные скрипты — гостевые книги на Perl, счётчики посещений и формы обратной связи.

В целом, CGI быстро стал стандартом динамического веба, так как его поддерживали все популярные веб-серверы (Apache, IIS и другие). Казалось бы, всё идеально… пока трафик не начал расти.
Изначально CGI-сайты были небольшими, и модель «один процесс на запрос» не вызывала никаких проблем. Но стоило нагрузке немного подрасти, проявились серьёзные недостатки.
Во-первых, каждый новый HTTP-запрос порождал внешний скриптовый процесс (из-за той самой изоляции), а по завершении он уничтожался. Если пользователи одновременно отправляли десятки запросов (а тем более сотни или тысячи), сервер прождал множество процессов.

Во-вторых, оверхед на создание процесса был огромен. Системе приходилось каждый раз загружать интерпретатор, инициализировать окружение, открывать файлы, и всё это было ради одного короткого ответа. В результате большая часть ресурсов CPU тратилась впустую на постоянный запуск/закрытие внешних программ. Неудивительно, что при высокой посещаемости CGI начинал «задыхаться».
В-третьих, CGI был неспособен к эффективному повторному использованию ресурсов. Скрипт должен обратиться к базе данных, в режиме CGI он при каждом запуске открывал новое соединение к БД, запрашивал данные и завершал работу. Следующий запрос — снова новый процесс, новое подключение к базе, а затем повторное чтение одних и тех же конфигураций. Не было никакого кэширования в памяти и никакого сохранения состояния. Программа каждый раз стартовала с нуля.

Из-за этого веб-приложения не могли держать объекты в памяти, а открытое соединение или результат предыдущих вычислений невозможно было переиспользовать. Это упрощало разработку, но больно било по производительности под нагрузкой.
В конечном счёте, к концу 90-х CGI уже не тянул высоконагруженный веб. Если сайт становился популярным, прирост трафика вызывал лавину процессов, а они, в свою очередь, забивали систему и тратили и так ограниченные ресурсы тех времён. Вот он, рецепт интернет-катастрофы — нужен был новый подход.
Из-за того, что проблема касалась всех, несколько решений появились почти одновременно. Разработчики веб-серверов решили сделать так, чтобы код выполнялся внутри самого сервера, минуя запуск внешних процессов. Так появились проприетарные серверные API, например, NSAPI от Netscape и ISAPI от Microsoft.

Apache изначально был мультипроцессным, но CGI всё равно требовал запуска отдельной внешней программы на каждый запрос. Чтобы убрать этот оверхед, Apache тоже обзавёлся своим API для модулей — вместо того, чтобы каждый раз запускать новую программу, он позволял загружать код расширений в память сервера и вызывать его напрямую при запросах. Позднее на основе таких API появились модули вроде mod_perl и mod_php, которые фактически встраивали интерпретатор Perl или PHP прямо в процессы веб-сервера. За счёт этого скрипты выполнялись заметно быстрее.
Эта архитектура набрала популярность в начале нулевых. Достаточно было установить модуль, и можно крутить динамический сайт без внешних CGI-программ. Однако у так называемого (мной) модульного подхода были свои минусы:
утечки памяти в модулях накапливались от запроса к запросу, потому что воркер жил долго — зависший модуль мог превратить воркер в зомби-процесс, который было сложно контролировать,
зачастую модули были привязаны к языку (писались на C/C++), и код приложений должен быть потокобезопасным,
в условиях виртуального
Разработчикам стало тяжелее писать, а

Нужен был компромисс, который сохранил бы плюсы CGI (простоту и изоляцию), но убрал главный его минус — запуск процесса на каждый запрос. И такое решение появилось.
В 1996 году компания Open Market предложила FastCGI. Идея разработчиков состояла в том, чтобы внешняя программа запускалась один раз и продолжала работать, обслуживая множество запросов подряд. То есть вместо того, чтобы «убивать» процесс после каждой страницы, его держали живым и переиспользовали.

Интересно, что изначально FastCGI разрабатывался как открытое расширение CGI, устраняющее узкое место с производительностью без отказа от самой модели взаимодействия. Но после стал отдельным протоколом.
В отличие от CGI, нужный FastCGI-процесс запускается при старте веб-сервера или при первом запросе. Веб-сервер вместо прямого запуска программы устанавливает с ней соединение (через Unix-сокет или TCP) и передаёт данные запроса в уже запущенный процесс. FastCGI-процесс получает запрос, обрабатывает его и затем возвращается в режим ожидания следующего запроса.
Один и тот же процесс может последовательно обработать сотни и тысячи запросов, порождая новые процессы лишь для параллельности или резервирования. Так устраняется главная проблема CGI — все инициализации (загрузка интерпретатора, подключение к базе, чтение конфигов) происходят один раз при старте FastCGI-пула.

FastCGI в буквальном смысле сохранил архитектуру «процесс вне сервера», но сделал её долгоживущей. Это дало сразу несколько преимуществ:
произошёл скачок в производительности — CGI-скрипт упирался в 5–10 запросов в секунду, а FastCGI на момент создания уже выдавал десятки и сотни,
сохранилась независимость языка — FastCGI-приложение может быть написано на любом языке, поддерживающем сокеты (таких подавляющее большинство),
осталась изоляция процессов — отдельный процесс, падение или утечка памяти в нём не приведут к падению веб-сервера и не затронут другие приложения,
появилась гибкость в развёртывании и горизонтальное масштабирование — FastCGI-процессы могут работать не только на той же машине, что и веб-сервер, но и на внешних серверах через TCP/IP.
Стоит отметить, что безопасность и изоляция с FastCGI относительно mod_php et al тоже улучшились. Поскольку FastCGI-демоны запускаются вне веб-сервера, их можно запускать от имени разных системных пользователей, ограничивать chroot-джейлами и прочими методами. Например,

Конечно, за всё приходится платить. Долгоживущие FastCGI-процессы означают, что в памяти в ожидании запросов постоянно висит рабочий пул, увеличивая базовое потребление памяти. Например, пул из десяти FastCGI-процессов может потреблять сотни мегабайт RAM, даже когда трафика почти нет. К слову, CGI же ничего не ел в простое, ведь процессы просто отсутствовали.
Ещё разработчикам нужно было переписывать или адаптировать свой код для таких процессов. Приходилось исправлять утечки памяти и учитывать глобальные состояния, которые при CGI-сценарии сбрасывались после каждого запуска. Но для проектов с большой нагрузкой выбор был очевиден — лучше выделить лишние 200 мегабайт памяти, чем бесполезно тратить половину мощности процессора на постоянные запуски и завершения процессов.

Спустя годы технология FastCGI незаметно вошла во многие архитектуры. Особо заметно её влияние на примере PHP-хостинга. Сначала PHP работал как модуль Apache (mod_php) или через CGI. Когда нагрузка становилась большой, mod_php на Apache начинал буксовать, поэтому в 2008–2010 годах многие крупные проекты в Рунете перешли на связку Nginx + PHP-FPM (это, по сути, и есть FastCGI-пул для PHP). На Хабре до сих пор можно найти истории [2] тех лет о том, как переход с Apache на FastCGI давал заметный прирост производительности.
Аналогичные решения появились практически во всех экосистемах. Даже приложения на Python работают через WSGI-совместимые серверы (gunicorn, uWSGI) — архитектурно это очень похоже на FastCGI, только протокол другой.
CGI проиграл битву за высоконагруженный веб. Как только появилась возможность избежать создания процесса на каждый запрос, практически все крупные проекты начали уходить — кто на FastCGI, кто на встроенные интерпретаторы вроде mod_php.
Сегодня трудно встретить серьёзный веб-сайт, который бы генерировался настоящими CGI-скриптами, запускаемыми при каждом обращении. Если только это не какой-то legacy-скрипт или очень небольшой внутренний инструмент, где производительность не критична.

Однако нельзя сказать, что CGI мёртв, так как его наследие живёт повсюду. Кроме того, популярные веб-серверы до сих пор поддерживают запуск CGI-скриптов на случай простых сценариев.
Да и в Unix-мире сама модель «для каждого запроса отдельный процесс» никуда не делась и не денется. Интересно, что в эпоху FaaS главная идея CGI возродилась в новом виде — в облаке изолированный контейнер для обработки события поднимается, выполняется и утилизируется. Только происходит это теперь в масштабах ЦОД и куда эффективнее, чем CGI на одном сервере.

В обычной же веб-разработке CGI сегодня — редкость. Большинство языков либо работают через постоянные процессы, либо вообще сами являются постоянными сервисами. Например, приложению на Node.js или Java не нужен отдельный веб-сервер — оно само слушает порт и работает постоянно. То есть подход, противоположный CGI, окончательно победил.
В свою очередь, FastCGI сегодня практически повсюду — он работает под капотом nginx, обслуживает PHP-пулы и крутит бэкенды — просто его мало кто замечает. А ведь без него и решений, которые пошли тем же путём, современные веб-сервисы появились бы значительно позже. Или не появились бы вовсе, потому что при первом же всплеске трафика веб бы просто «захлебнулся» пятисотыми ошибками.
Если у вас в продакшене где-то до сих пор крутится CGI-скрипт на Perl, не стесняйтесь, расскажите в комментариях. Тут не осуждают…
© 2026 ООО «МТ ФИНАНС»
Автор: SrvTrantor
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nginx/447112
Ссылки в тексте:
[1] хостинга: https://www.reg.ru/?rlink=reflink-717
[2] истории: https://habr.com/ru/articles/106311/
[3] Источник: https://habr.com/ru/companies/ruvds/articles/1010078/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1010078
Нажмите здесь для печати.