Веб-сервер: различия между версиями

Материал из CAMaaS preliminary wiki
Перейти к навигации Перейти к поиску
 
(не показано 15 промежуточных версий 2 участников)
Строка 1: Строка 1:
Веб-сервер должен принимать HTTP запросы, формируемые JS-скриптом на стороне клиента и содержащие запакованные входные параметры вызова серверной функции и идентификатор этой функции. Сервер, в свою очередь, должен самостоятельно выполнять соответствуюую операцию, либо делегировать вызов куда-то еще. При завершении обработки сервер должен возвращать ответы браузеру, который отображает их для пользователя.
{{NavBar|{{Системная иерархия:Веб-сервер}} }}


Дабы сделать реализацию обработчика HTTP-запросов общей, нужно серверу предоставлять динамическую библиотеку, содержащую известную ему точку входа. Эта точка входа на основе переданного ей идентификатора (изначально указанного клиентом в HTTP запросе) выбирает конкретную функцию для выполнения, распаковывает соответствующим образом (т.е. в соответствии с интерфейсом функции) параметры и вызывает ее.
Веб-сервер должен принимать HTTP запросы, формируемые JS-скриптом на стороне клиента и содержащие запакованные входные параметры вызова серверной функции и идентификатор этой функции. В зависимости от значения идентификатора функции сервер должен обрабатывать запрос самостоятельно<ref>зачем?</ref> либо делегировать вызов внутрипроцессному серверу (DLL, SO) с известной точкой входа:
 
Пока набор конкретных функций можно не рассматривать, и задать лишь интерфейс обобщенной точки входа:


<source lang=c>
<source lang=c>
return_code entry_point(
return_code entry_point( //возвращается код ошибки chsvlib (ноль в случае успешного завершения)
   uint ID, //идентификатор операции
   uint ID, //идентификатор операции
   byte in_params[in_byte_count], //набор байт, содержащий упакованные входные параметры
   byte in_params[in_byte_count], //набор байт, содержащий упакованные входные параметры
   uint in_byte_count, //байтовый размер упакованных данных
   uint in_byte_count, //байтовый размер упакованных данных
   byte out_params[out_byte_count], //буфер, предоставляемый nginx'ом для хранения выходных параметров вызываемой функции
   byte** out_params[out_byte_count], //буфер, который выделяется внутрипроцессным сервером и инициализируется ответом на запрос.
   uint*  out_byte_count //На входе - размер буфера out_params, на выходе - фактический размер выходных данных
   uint*  out_byte_count //На выходе - размер выходных данных
);
);
</source>
</source>


[[Файл:Web-server-interface.jpg|500px|thumb|интерфейсы]]
Внутрипроцессный сервер, в свою очередь, выполняет функции [[Подсистема управления | управляющей подсистемы]].
Также задана функция освобождения памяти, выделенной внутрипроцессным сервером:
<source lang=c>
return_code_t FreeData( //возвращается код ошибки chsvlib (ноль в случае успешного завершения)
  void* pData // указатель на буфер, выделенный внутрипроцесным сервером,
              // т.е. возвращенный точкой входа entry_point через параметр out_params
);
</source>


Эта функция, предоставляемая so библиотекой, должна вызываться по следующей схеме:
Коды ошибок, возвращаемых функциями внутрипроцессного сервера, могут быть преобразованы в текстовое описание ошибки с помощью следующей функции:
<source lang=c>
const char* GetErrorDescription( // возвращается read-only utf-8 C-строка, содержащая текстовое описание ошибки
  uint code, // код ошибки, на основе которого формируется текстовое описание
  uint* pSize // опциональный указатель на 32-битовую переменную, которой функцией присваивается длина строки в байтах. Если длина не нужна, параметр может быть NULL.
);
</source>


1. Получение размера выходных данных:
Кроме этого, возможно ведение журнала ошибок, возникающих на стороне управляющей подсистемы (но не на стороне предметной области, если та не возвращает коды ошибок управляющей подсистеме).
Следующая функция позволяет задать файл журнала ошибок в текстовом формате (utf-8) веб-сервером для записи туда событий об ошибок, возникающих на стороне внутрипроцессного сервера.
<source lang=c>
<source lang=c>
uint output_size = 0;
return_code_t SetErrorLogFile( // возвращается код ошибки chsvlib (ноль в случае успешного завершения)
entry_point(operation_id, input_parameters, input_parameters_size, 0, address_of(output_size));
  const char* pszFileName, //относительный или абсолютный путь к файлу журнала. Строка должна либо завершаться терминальным нулем (в этом случае cchFileName должен быть равен -1),
                            //либо должна быть длины, равной значению cchFileName.
  std::size_t cchFileName, //длина строки pszFileName, либо -1, если строка заканчивается терминальным символов
  bool fEraseContent //флаг удаления содержимого файла, если файл уже существует. Если файл существует, и флаг сброшен, сервер будет дополнять его новыми данными
);
</source>
</source>


2. Выделение памяти для выходных параметров веб-сервером и вызов функции еще раз:
==Пример запроса к внутрипроцессному серверу==
<source lang=c>
 
byte output[output_size];
<source lang=cpp>
entry_point(operation_id, input_parameters, input_parameters_size, output, address_of(output_size));
auto simulation = CAMaaS::make_package(CAMaaS::sequence_to_pack("Node name"), CAMaaS::sequence_to_pack(strModelName));
char* pProcessName;
std::uint32_t cchProcessName;
std::uint32_t nErr = entry_point(StartSimulationId, simulation.data(), simulation.size(), (void**) &pProcessName, &cchProcessName);
if (nErr != 0)
{
  std::cerr << "Error " << nErr << " was returned by the server: " << GetErrorDescription(err, nullptr) << ".\n";
  return "";
}else
{
  auto ret = std::string(pProcessName, cchProcessName);
  FreeData(pProcessName);
  return ret;
}
</source>
</source>


3. Передача выходных параметров клиенту, который их распоковывает и отображает пользователю.
==Установка==
1. Для установки веб-сервера необходимо загрузить следующие пакеты из репозитория с помощью команды apt-get:
<code>sudo apt-get install nginx uwsgi postgresql python-psycopg2 git python-dev uwsgi-plugin-python</code>
 
2. Скачиваем свежий репозиторий django:
<code>git clone git://github.com/django/django.git django-trunk</code>
 
3. Добавляем в файл <code>/usr/lib/python2.7/dist-packages/django.pth</code> строку с абсолютным путем до скачанного репозитория
 
4. Делаем для удобства ссылку в /usr/local/bin на django-admin.py с помощью команды ln.
 
5. Установим утилиту setuptools:
<code>wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python</code>
 
6. В папке репозитория django запустим скрипт для установки:
<code>sudo python setup.py build install</code>
 
7. Настроим postgresql на прием всех запросов на порт 5433, для этого в файле /etc/postgresql/9.3/main/postgresql.conf поправим строчку <code>listen_addresses = '*'</code> и <code>port = 5433</code>, затем перезапустим командой <code>sudo service postgresql restart</code>.


[[Файл:Web-server-flow.png|thumb|500px|обращения]]
8. Заходим в psql под пользователем postgres с помощью команды <code>sudo -u postgres psql</code> и создаем базу данных командой <code>CREATE DATABASE chsv_test WITH OWNER postgres ENCODING 'utf-8';</code>, затем меняем пароль пользователя postgres командой <code>\password <chsvtest></code> и выходим командой <code>\q</code>


Написал простенькую so и клиент этой so, демонстрирующую такой протокол на примере простых типов данных и трех простеньких операций. Пока залил на гугл диск:
9. Переходим в репозиторий django, создаем новый проект командой <code>django-admin startproject CAMaaS1_web</code>, затем копируем в создавшуюся папку содержимое репозитория web-сервера.
https://drive.google.com/open?id=0B7qfTPtc54icQ3A2MWllbjhHaDQ.

Текущая версия на 09:01, 5 июля 2016

Компоненты на верхнем уровне \ Веб-сервер

Веб-сервер должен принимать HTTP запросы, формируемые JS-скриптом на стороне клиента и содержащие запакованные входные параметры вызова серверной функции и идентификатор этой функции. В зависимости от значения идентификатора функции сервер должен обрабатывать запрос самостоятельно[1] либо делегировать вызов внутрипроцессному серверу (DLL, SO) с известной точкой входа:

return_code entry_point( //возвращается код ошибки chsvlib (ноль в случае успешного завершения)
  uint ID, //идентификатор операции
  byte in_params[in_byte_count], //набор байт, содержащий упакованные входные параметры
  uint in_byte_count, //байтовый размер упакованных данных
  byte** out_params[out_byte_count], //буфер, который выделяется внутрипроцессным сервером и инициализируется ответом на запрос.
  uint*  out_byte_count //На выходе - размер выходных данных
);

Внутрипроцессный сервер, в свою очередь, выполняет функции управляющей подсистемы. Также задана функция освобождения памяти, выделенной внутрипроцессным сервером:

return_code_t FreeData( //возвращается код ошибки chsvlib (ноль в случае успешного завершения)
   void* pData // указатель на буфер, выделенный внутрипроцесным сервером,
               // т.е. возвращенный точкой входа entry_point через параметр out_params
);

Коды ошибок, возвращаемых функциями внутрипроцессного сервера, могут быть преобразованы в текстовое описание ошибки с помощью следующей функции:

const char* GetErrorDescription( // возвращается read-only utf-8 C-строка, содержащая текстовое описание ошибки
   uint code, // код ошибки, на основе которого формируется текстовое описание
   uint* pSize // опциональный указатель на 32-битовую переменную, которой функцией присваивается длина строки в байтах. Если длина не нужна, параметр может быть NULL.
);

Кроме этого, возможно ведение журнала ошибок, возникающих на стороне управляющей подсистемы (но не на стороне предметной области, если та не возвращает коды ошибок управляющей подсистеме). Следующая функция позволяет задать файл журнала ошибок в текстовом формате (utf-8) веб-сервером для записи туда событий об ошибок, возникающих на стороне внутрипроцессного сервера.

return_code_t SetErrorLogFile( // возвращается код ошибки chsvlib (ноль в случае успешного завершения)
   const char* pszFileName, //относительный или абсолютный путь к файлу журнала. Строка должна либо завершаться терминальным нулем (в этом случае cchFileName должен быть равен -1),
                            //либо должна быть длины, равной значению cchFileName.
   std::size_t cchFileName, //длина строки pszFileName, либо -1, если строка заканчивается терминальным символов
   bool fEraseContent //флаг удаления содержимого файла, если файл уже существует. Если файл существует, и флаг сброшен, сервер будет дополнять его новыми данными
);

Пример запроса к внутрипроцессному серверу

auto simulation = CAMaaS::make_package(CAMaaS::sequence_to_pack("Node name"), CAMaaS::sequence_to_pack(strModelName));
char* pProcessName;
std::uint32_t cchProcessName;
std::uint32_t nErr = entry_point(StartSimulationId, simulation.data(), simulation.size(), (void**) &pProcessName, &cchProcessName);
if (nErr != 0)
{
   std::cerr << "Error " << nErr << " was returned by the server: " << GetErrorDescription(err, nullptr) << ".\n";
   return "";
}else
{
   auto ret = std::string(pProcessName, cchProcessName);
   FreeData(pProcessName);
   return ret;
}

Установка

1. Для установки веб-сервера необходимо загрузить следующие пакеты из репозитория с помощью команды apt-get: sudo apt-get install nginx uwsgi postgresql python-psycopg2 git python-dev uwsgi-plugin-python

2. Скачиваем свежий репозиторий django: git clone git://github.com/django/django.git django-trunk

3. Добавляем в файл /usr/lib/python2.7/dist-packages/django.pth строку с абсолютным путем до скачанного репозитория

4. Делаем для удобства ссылку в /usr/local/bin на django-admin.py с помощью команды ln.

5. Установим утилиту setuptools: wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python

6. В папке репозитория django запустим скрипт для установки: sudo python setup.py build install

7. Настроим postgresql на прием всех запросов на порт 5433, для этого в файле /etc/postgresql/9.3/main/postgresql.conf поправим строчку listen_addresses = '*' и port = 5433, затем перезапустим командой sudo service postgresql restart.

8. Заходим в psql под пользователем postgres с помощью команды sudo -u postgres psql и создаем базу данных командой CREATE DATABASE chsv_test WITH OWNER postgres ENCODING 'utf-8';, затем меняем пароль пользователя postgres командой \password <chsvtest> и выходим командой \q

9. Переходим в репозиторий django, создаем новый проект командой django-admin startproject CAMaaS1_web, затем копируем в создавшуюся папку содержимое репозитория web-сервера.

  1. зачем?