Ускорение WEB приложения бинарными компонентами

Компилируемые части интерпретируемых языков

Современные приложения как правило написаны на скриптовых языках, таких как php, asp, java script, ruby, python и т.д. И не смотря на всевозможные оптимизации, скритповые языки остаются медленнее компилируемых. Так почему бы не вынести тяжелую логику в компилируемую программу?

Могу привести еще один пример зачем нужен данный подход. В одном из моих проектов есть логика, которая запускает компиляцию приложения. В php для подобных случаев предусмотрен ряд функций таких как exec() или system() и других. Но это не хорошо с точки зрения безопасности. В данном случае мне помогла разработка модуля для php, реализующий фукцию exec() но для конкретного приложения. В исходнике это выглядело примерно так: $builder = startBuild(); теперь даже если злоумышленник получит доступ к исходникам, то пострадать сможет только то, с чем работает данная функция.

Модули php

Итак, у нас есть тяжелый процесс и мы хотим вынести его в компилируемый модуль. Модуль предствляет из себя обычную разделяемую библиотеку (*.so для *nix, *.dll для windows). Язык разработки для расширений php - c.

Для создания расширения достаточно 3х файлов:

config.m4

 
PHP_ARG_ENABLE(hello, whether to enable Hello World support,
[ --enable-hello Enable Hello World support])

if test "$PHP_HELLO" = "yes"; then
AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h

 
#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1

#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_FUNCTION(hello_world);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

php_hello.h

 
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_hello.h"

static function_entry hello_functions[] = {
  PHP_FE(hello_world, NULL)
  {NULL, NULL, NULL}
};

zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
  STANDARD_MODULE_HEADER,
#endif
  PHP_HELLO_WORLD_EXTNAME,
  hello_functions,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
#if ZEND_MODULE_API_NO >= 20010901
  PHP_HELLO_WORLD_VERSION,
#endif
  STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

PHP_FUNCTION(hello_world)
{
  RETURN_STRING("Hello World", 1);
}

Данное расширение, hello не особо полезно, оно выводит строку Hello World. Но это всего лишь скелет.

Собрать расширение можно коммандами:

 
$ phpize
$ ./configure
$ make
$ make install

После чего добавить в php.ini строку, подключающую расширение


extension=hello.so

Теперь в php скрипте можно вызвать функцию hello_world().

Конечно ради вывода одной строчки не стоит создавать целое расширение. В моем примере модуль запускал программу, написанную на go. А последняя уже и с базой данных работала и занималась серьезными вычислениями. Так же никто не мешает написать на любимом языке, хоть на паскале разделяемую библиотеку и обращаться к ней из расширения.

CGI cкрипты

Еще один способ запустить программу - использовать CGI или FastCGI. Вот пример кода на C, взятый из википедии:


#include 
int main(void) {
  printf("Content-Type: text/plain;charset=us-ascii\n\n");
  printf("Hello, world!\n\n");
  return 0;
}

Хотя мудиль php и CGI совершенно разные технологии и создавались они для совершенно разных задач, в данном случае их можно сравнить. Самая глобальная в нашем случае разница между использованием расширения и использованием CGI в том, что для запуска программы через CGI создается новое HTTP соединение. т.е. модуль php удобнее использовать во момент выполнения скрипта, а CGI вызывать из клиентской частик, к примеру по клике на кнопку. А это означает что можно вынести часть вычислительных процессов на отдельный сервер, не используя различные методы межсерверного взаимодействия.

Я видел в сети амбициозные проекты на C++, реализующий целую CMS по технологии CGI.