Каталог решений - Внешняя компонента как REST-API-компонента…

Внешняя компонента как REST-API-компонента…

Внешняя компонента как REST-API-компонента…

В наличии

…и совсем немного кода на С[++]…

Категория:

Описание

Всем привет!

 

ВВЕДЕНИЕ

Я в последнее время делал несколько проектов на Python, суть которых сводилась к внешнему сервису, работающему как очередь оборудования и механизм контроля. Но Python — это хоть и очень просто, но не так быстро, поэтому время от времени я пытался найти, как сделать какой-нить сервис на С[++] (плюсы в скобках как бы намекают, что от плюсов там по большому счету только int перед main). В итоге нашел, что позволило мне все свои механизмы, написанные на Python, достаточно легко и непринужденно переработать в механизмы, работающие на С++.

 

БИБЛИОТЕКА RESTBED

Периодически набирая в гуглах что-то типа "HTTP-сервер на С++", я натыкался на разные решения с костылями и палками, но в последний раз наткнулся на отличную (на мой скромный взгляд) библиотеку, которая достаточно просто позволяет организовать HTTP-сервис, и при этом (и это было важно) существует и отлично работает даже на DEBIAN для Rispberry PI (ниже приведу сравнительный тест).

Сама по себе библиотека очень проста и в сути своей оперирует всего несколькими сущностями: настройкой соединения и ссылкой на сервисы. У библиотеки достаточно большой функционал (включая авторизацию, SSL/HTTPS, многопоточность и все то, что еще может нам потребоваться).

 

ПРИМЕР ПРОСТОГО СЕРВИСА

Давайте замутим простой сервис, добавляющий в некий массив значения и возвращающий нам, есть ли такое значение в этом массиве.

Для начала установим соответствующую библиотеку (для Linux, как это сделать в винде — я без понятия, если, конечно, что не WSL[2]).

  sudo apt install librestbed-dev librestbed0

Тут у нас два пакета — сама библиотека и ее заголовочные файлы для разработчиков.

Дальше при сборке нам достаточно будет указать опцию "-lrestbed" и все.

Напишем простой код:

 

ЗАГОЛОВКИ

  #include <stdlib.h>
  #include <map>
  #include <string>
  #include <memory>
  #include <cstdlib>
  #include <restbed>

1. Стандартная библиотека — нужна нам для преобразования параметра командной строки в число функцией atoi (для указания номера порта, на котором весить сервис).

2. Map — "ассоциативный массив", который всегда упорядочен по ключу. Скорость доступа к элементуO( Log2N ).

3. Строки — куда без них…

4. Умные указатели — в нашем случае это shared_ptr, который… Сами где-нить прочитайте, а то это может быть надолго. В нашем случае понимать всю суть этого не нужно.

5. Честно говоря, сам с этим не разбирался. Но нам пока это тоже не нужно.

6. Ну и сама наша библиотека для организации HTTP-сервиса.

И еще немного кода…

  using namespace std;
  using namespace restbed;
  
  map <string, string> srvArray;

Собственно, это у нас определение пространств имен (чтобы не писать перед каждой функцией и типом std::) и нашего "ассоциативного массива".

 

"ХЭНДЛЕРЫ"

  void get_set_method_handler( const shared_ptr< Session > ss )
  {
      const auto req = ss->get_request();
      auto data = req->get_path_parameter( "data" );
      srvArray[ data ] = req->get_path_parameter( "value" );
      ss->close( OK, "OK", { { "Content-Length", "2" } } );
  }
  
  void get_get_method_handler( const shared_ptr< Session > ss )
  {
      const auto req = ss->get_request();
      auto data = req->get_path_parameter( "data" );
      if (!srvArray[ data ].empty()) {
          const string ret = srvArray[ data ];
          //cout << ret << endl;
          ss->close( OK, ret, { { "Content-Length", ::to_string( ret.size() ) } } );
      } else {
          //cout << "EMPTY" << endl;
          ss->close( OK, "EMPTY", { { "Content-Length", "5" } } );
      }
  }

Здесь у нас два обработчика событий HTTP-сервиса, которые мы чуть ниже зарегистрируем.

Первый обработчик устанавливает ключ и значение по приехавшим параметрам. Второй обработчик возвращает установленный ранее параметр или строку "EMPTY", если такое значение мы еще не устанавливали.

Алгоритм тут достаточно прост:

1. Получаем из сессии (аргумент функции) текущий запрос (в принципе, все как в 1С).

2. Получаем из запроса именованный параметр(ы) (задается в шаблоне, увидите ниже).

3. Устанавливаем в ключ значение или извлекаем значение по ключу.

4. Возвращаем "ОК, значение или "ENPTY" (если значение с таким ключом еще не было установлено).

Ну и давайте перейдем к самому интересному…

 

ФУНКЦИЯ MAIN

  int main ( const int carg, const char** arg)
  {
      int port = 8080;
      if ( carg > 1 ) port = atoi( arg[1] );
  
      auto resource_set = make_shared< Resource >();
      resource_set->set_path( "/set/{data: .*}/{value: .*}" );
      resource_set->set_method_handler( "GET", get_set_method_handler );
  
      auto resource_get = make_shared< Resource >();
      resource_get->set_path( "/get/{data: .*}" );
      resource_get->set_method_handler( "GET", get_get_method_handler );
  
      auto settings = make_shared< Settings >();
      settings->set_port( port );
      settings->set_default_header( "Connection", "close" );
  
      Service service;
      service.publish( resource_set );
      service.publish( resource_get );
  
      service.start( settings );
  }

Вот и вся программа на "супер-пупер-сложном" языке.

1. Указываем порт по умолчанию "8080".

2. Проверяем, есть ли параметры в командной строке.

3. Если параметры есть, то устанавливаем порт из первого (в действительности — второго).

4. Определяем первый наш HTTP-ресурс (SET — установка значения), как умный указатель с соответствующим типом значения.

5. Устанавливаем шаблон "/set/{data: .*}/{value: .*}". Я несколько минут потратил, пока до меня дошло, а Вы?

6. Определяем второй ресурс (GET — получение установленного ранее значения или "EMPTY").

7. Создаем настройку, передаем в нее порт, устанавливаем заголовки по умолчанию.

8. Создаем сервис и регистрируем там наши ресурсы.

9. Стартуем сервис с нашими настройками.

Ну и осталось извлечь пользу.

 

ДЕРНЕМ СЕРВИС ИЗ 1С

Ну тут тоже все просто, но чуть усложним и проведем нагрузочный тест.

 

has been added to your cart:
Оформление заказа