High performance
10 January 2011

libscgi — эффективное решение для простых и быстрых скриптов

Очень часто необходимо реализовать простое легкое решение, которое должно отработать довольно быстро. А с использованием технологии AJAX это стало еще актуальнее. Это может быть как скрипт автокомплита, скрипт специфического поиска, вывод информации из справочника. Ранее использовались cgi скрипты. При больших нагрузках они оказались не очень эффективными и были разработаны протоколы fcgi и scgi. Следует заметить что производительность scgi сервера довольно-таки высокоя (более 1500 запр/сек) и памяти занимает всего 600K.

Протокол Simple Common Gateway Interface (SCGI) — это протокол по взаимодействию приложений с веб (http) серверами. Большинство современных WEB-серверов (Apache/nginx/lighttpd) имеют встроенную поддержку scgi. Ниже дано краткое описание использование простой библиотечки, которая представляет собой scgi сервер.

Исходники тут.

Описание Протокола.

Клиент подключается к SCGI серверу по протоколу передачи потоков, допускающем передачу 8-ми битных байт. Запрос состоит из нескольких заголовков и тела запроса (POST DATA).Формат передачи данных запроса имеет следующий шаблон:[len header]: \r\n
[header parm name] \0 [header parm value] \0 \r\n
[header parm name] \0 [header parm value] \0 \r\n
...
,\r\n
[POST DATA]

  • len header — длинна данных заголовка
  • header parm name — название заголовка ( Header)
  • header parm value — значение заголовка

Первый заголовок обязан иметь имя «CONTENT_LENGTH», а в его значение должна быть указана длина тела сообщения (в десятичном представлении). Заголовок «CONTENT_LENGTH»должен всегда передаваться, даже если его значение 0 (все GET запросы). Между заголовками и телом запроса должна находится запятая и перевод строки. Тело ( POST/PUT данные ) пересылается за заголовками и его длина должна определяться заголовком «CONTENT_LENGTH».

Настройка nginx
В руководстве к каждому WEB серверу должна быть приложена инструкция, как подключить тот или иной модуль. Я настраивал свой scgi сервер под nginx.
Пример настройки на определенный location, порт 8080.
location /dictionary {
scgi_pass localhost:8080;
include scgi_params;
}

Внутреннее устройство scgi сервера и его использование

Сервер реализован на основе libevent. На каждый используемый url ( заголовок DOCUMENT_URI) вешается свой handler.
scgi.addHandler("/post", reinterpret_cast<IScgiHandler *>(new Handler1()));

Каждый пользовательский обработчик должен быть определен в методе run() наследника класса IScgiHandler.
В Данный метод передаются параметры:
map< string,string > * parms - входные параметры,
char * buffUot - выходной буфер.

Все полученные POST данные, хранятся в parms с ключом «POST_DATA».
Доступ к входным параметрам упрощен с помощью метода protected: getParam( string parnName, map< string,string > * parms);

рассмотрим пример кода обработчика:

  1.  
  2. class Handler1: public IScgiHandler {
  3.  
  4.         void run(map< string,string > * parms, char * buffUot) {
  5.                 string parm = getParam("REQUEST_METHOD",parms);
  6.                 // проверяем что метод POST
  7.                 if ( parm == "POST" ) {
  8.                         // возвращаем на WEB-client POST данные
  9.                         strcpy(buffUot, getParam("POST_DATA",parms).c_str());
  10.                         return;
  11.                 }
  12.                 // иначе возвращаем на WEB-client значение параметра QUERY_STRING
  13.                 strcpy(buffUot, getParam("QUERY_STRING",parms).c_str());                 
  14.         }
  15. };
  16.  
  17.  


Запуск сервера довольно-таки простой:
  1.  
  2.         // объявляем
  3.         scgiServer scgi;
  4.  
  5.         //если нам необходима демонизация
  6.         pid_t pid;
  7.         if ((pid = scgi.demonize()) < 1) {
  8.                 if (pid == -1) {
  9.                         cerr << "demonize error\n";
  10.                         return 1;
  11.                 }      
  12.                 return 0;
  13.         }
  14.  
  15.         // инициализируем сервер на прослушивание
  16.         if (scgi.init("127.0.0.1", 8080) ) {
  17.                 cerr << "server stopped\n";
  18.                 return 1;
  19.         }
  20.        
  21.         // добавляем пользовательские хандлеры
  22.         scgi.addHandler("/post", reinterpret_cast<IScgiHandler *>(new Handler1()));    
  23.         scgi.addHandler("/xxx",  reinterpret_cast<IScgiHandler *>(new Handler2()));
  24.  
  25.         scgi.run();
  26.  


Инсталляция

Библиотека реализавана как статическая.
make lib — компилится библиотека
make компилится файл примера

Компиляция Вашего приложения:
cp libscgi.a /usr/local/lib
g++ example.cpp -o scgi_server -Wall -g -L. -L/usr/local/lib -levent -lscgi

Ограничения:

Длинна POST данных и ответа должна быть не более 2К. Данное ограничение можно изменить, задав соответствующее значение константе #define BUFFSIZE и пересобрав библиотеку. Для моих задач этого хватает.
Библиотека экспериментальная, есть недоделки и возможны недочеты. Конструктивная критика приветствуется. Все что планируется сделать в TODO. Желающим протестировать и помочь разработке всегда рады.

+23
3.4k 59
Comments 40
Top of the day