Вдохновленный множественными статьями о микросервисах, пришла в голову идея, реализовать свой микро веб-сервис, но не стандартным путем.

Что такое CGI и FastCGI ?

CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером. Программу, которая работает по такому интерфейсу совместно с веб-сервером, принято называть шлюзом, хотя многие предпочитают названия «скрипт» (сценарий) или «CGI-программа».

Сам интерфейс разработан таким образом, чтобы можно было использовать любой язык программирования, который может работать со стандартными устройствами ввода-вывода. Такими возможностями обладают даже скрипты для встроенных командных интерпретаторов операционных систем, поэтому в простых случаях могут использоваться даже командные скрипты.

Например bash скрипт :

#!/bin/bash
echo "Content-Type: text/plain;charset=us-ascii\n\n";
echo "Hello, world!\n\n";

Все скрипты, как правило, помещают в каталог cgi (или cgi-bin) сервера, но это необязательно: скрипт может располагаться где угодно, но при этом большинство веб-серверов требуют специальной настройки. В веб-сервере Apache, например, такая настройка может производиться при помощи общего файла настроек httpd.conf или с помощью файла .htaccess в том каталоге, где содержится этот скрипт.

То есть, создав файл с раширением .cgi (например hello.cgi), наполнив его строками приведенными выше и поместив его в каталог cgi(или cgi-bin) и запустив веб-сервер потдерживаюший cgi с правильно-выставленными путями к исходным файлам *.cji, мы можем открыть веб-бразер и обратиться по адресу http://localhost/cgi-bin/hello.cgi. И результат будет : Hello, world!

Стандарный этапы выполнения cgi скрипта :

  • на сервер приходит HTTP запрос
  • сервер парсит этот запрос
  • создает изолированный набор переменных окружения (REQUEST_METHOD, CONTENT_LENGTH, QUERY_STRING и т.д.) и запускает cgi скрипт. (Как вы догадались, эти перменные мы и будет считывать нашим скриптом, это входящие данные, например данные запроса - QUERY_STRING)
  • после завершения скрипта, веб-сервер формирует ответ на основании полученного вывода в STDOUT и отправляет клиенту.

Плюсы :

  • как было сказано, подойдет любой язык(среда) который способен считывать и отдавать данные из обычного потоков ввода и вывода STDIN/STDOUT

Минусы :

  • каждый новый запрос формирует отдельный экзепляр скрипта (отдельный процесс), что сказывается на быстродействии

FastCGI - дальнейшее развитие технологии CGI. По сравнению с CGI является более производительным и безопасным.

Отличия от CGI :

  • Недостаток CGI-программ в том, что они должны быть перезапущены веб-сервером при каждом запросе, что приводит к понижению производительности. FastCGI, вместо того чтобы создавать новые процессы для каждого нового запроса, использует постоянно запущенные процессы для обработки множества запросов. Это позволяет экономить время.
  • В то время как CGI-программы взаимодействуют с сервером через STDIN и STDOUT запущенного CGI-процесса, FastCGI-процессы используют Unix Domain Sockets или TCP/IP для связи с сервером. Это даёт следующее преимущество над обычными CGI-программами: FastCGI-программы могут быть запущены не только на этом же сервере, но и где угодно в сети. Также возможна обработка запросов несколькими FastCGI-процессами, работающими параллельно.

FastCGI может быть использован в любом языке, поддерживающем сокеты. Есть большое разнообразие библиотек для разных языков программирования.

CGI + Java

Начнем с простого. Как было описано в первых этапах, нам необходим механизм считывания переменных окружения, для того что бы знать входящие параметры HTTP запроса. Мы помним что веб-сервер создает эти переменные в изолированном виде для каждого процесса, и что простого вызова System.getProperty(“cgi.query_string”) будет недостаточно. Мы должны передать эти переменные java программе, а если быть точным, то JVM процессу. Для этого создадим скромный bash скрипт, который будет запускать java и передавать нужные параметры :

#!/bin/sh
java \  
-Dcgi.content_type=$CONTENT_TYPE \
-Dcgi.content_length=$CONTENT_LENGTH \
-Dcgi.request_method=$REQUEST_METHOD \   
-Dcgi.query_string=$QUERY_STRING \
-Dcgi.server_name=$SERVER_NAME \  
-Dcgi.server_port=$SERVER_PORT \
-Dcgi.script_name=$SCRIPT_NAME \
-Dcgi.path_info=$PATH_INFO \
Hello

После, соответственно написать саму java программу (hello.java), скомпилировать и положить рядом. Для удобства я использовал библиотеку cgi_lib, методы которой инкапсулируют логику чтения и генерации ответа.

import java.util.*; 
import java.io.*;
public class Hello {
  public static void main( String args[]){
      //
      //  Here is a minimalistic CGI program that uses cgi_lib
      //
      //  Print the required CGI header.
      //
      System.out.println(cgi_lib.Header());
      //
      //  Parse the form data into a Hashtable.
      //
      Hashtable form_data = cgi_lib.ReadParse(System.in);
      //
      // Create the Top of the returned HTML page
      //
      String name = (String)form_data.get("name");
      System.out.println(cgi_lib.HtmlTop("Hello There " + name + "!"));
      System.out.println("&lth1 align=center&gtHello There " + name +
                         "!</h1>");
      System.out.println("Here are the name/value pairs from the form:");
      //
      //  Print the name/value pairs sent from the browser.
      //
      System.out.println(cgi_lib.Variables(form_data));
      //
      //  Print the Environment variables sent in from the Unix script.
      //
      System.out.println("Here are the CGI environment variables/value pairs" +
                         "passed in from the UNIX script:");
      System.out.println(cgi_lib.Environment());
      //
      // Create the Bottom of the returned HTML page to close it cleanly.
      //
      System.out.println(cgi_lib.HtmlBot());
  }
}

На самом деле методы класса cgi_lib используют функции System.getProperty() для чтения входящих данных, а поток вывода System.out.println() для формирования ответа.

Вот и все. Осталось проверить результат по адресу http://localhost/cgi-bin/Hello.cgi.

FastCGI + Java

С FastCGI придется немножко повозиться, но результат эта реализация даст лучше, потому что во первых, процесс запускается только единожды, во вторых, можно настроить лоад балансер, в третьих само приложение может находиться по совершенно другому ip адресу.

  • Выкачать исходники этой библиотеки
  • Распаковать и перейти в директорию java, где мы и будем создавать веб-приложение
  • Создать Класс TinyFCGI.java
import com.fastcgi.FCGIInterface;
import java.io.*;
import static java.lang.System.out;

class TinyFCGI {
    public static void main (String args[]) {
        int count = 0;
        FCGIInterface fcgiinterface = new FCGIInterface();
        while(fcgiinterface.FCGIaccept() >= 0) {
            count++;
            out.println("Content-type: text/html\n\n");
            out.println("<html>");
            out.println(
                "<head><TITLE>FastCGI-Hello Java stdio</TITLE></head>");
            out.println("<body>");
            out.println("<H3>FastCGI-HelloJava stdio</H3>");
            out.println("request number " + count +
                               " running on host "
                               + System.getProperty("SERVER_NAME"));
            out.println("</body>");
            out.println("</html>");
        }
    }
}
  • скомпилировать все java файлы находящиеся в директории java
  • запустить приложение java -DFCGI_PORT=9884 TinyFCGI (помним что FastCGI-процессы используют Unix Domain Sockets или TCP/IP для связи с сервером.)
  • теперь осталось настроить Apache установив моудль mod_proxy_fcgi
  • сделать доступным sudo a2enmod proxy_fcgi
  • создать файл конфигурации /etc/apache2/conf-enabled/your_site.conf
<Location /your_site>
  ProxyPass fcgi://localhost:9884/
</Location>
  • и перезапустить сервер sudo apache2ctl restart

Посмотреть что же у нас получилось можно по адресу - http://localhost/your_site.

Вывод

Конечно *CGI интерфейс не заменит вам полноценного JavaEE сервера или сервлет контейнера, но я бы сказал, что это простой и альтернативный путь к созданию веб-приложения “на коленке”(микро-сервис) и используя не только Java, но и тот язык, который вы довольно хорошо знаете.