Dans cet article, je vais m'appuyer sur l'utilisation de libevent dans debian + gcc + cmake , mais sur d'autres systèmes d'exploitation de type Unix, il ne devrait y avoir aucune difficulté (pour Windows, vous devrez construire à partir des sources et modifier FindLibEvent.cmake déposer)
Préface
Je développe des microservices depuis environ 3 ans, mais je n'avais pas une compréhension initiale d'une pile technologique appropriée. J'ai essayé de nombreuses approches différentes (dont certaines étaient OpenDDS et apache- thrift ) mais ont fini par s'installer sur RestApi .
RestApi communique via des requêtes HTTP, qui représentent à leur tour la structure de données des en-têtes et des corps de requête transmis via un socket. La première chose que j'ai remarquée a été boost / asio, qui fournit des sockets TCP, mais il y a des difficultés avec la quantité de développement:
Il est nécessaire d'écrire la réception correcte des données sur le socket
Analyse d'en-tête auto-écrite
Analyse auto-écrite des paramètres GET
Routage de chemin
La deuxième ligne était POCO (POCKET Fabrications), qui dispose d' un serveur HTTP plus haut niveau, mais il y avait encore un problème avec un tas de fonctionnalités écrites auto. De plus, cet outil est un peu plus lourd et fournit des fonctionnalités qui ne sont peut-être pas nécessaires (il surcharge un peu nos microservices). POCO est orienté vers d'autres tâches que les microservices.
Par conséquent, parlons plus loin de libevent sur lequel je me suis arrêté.
Pourquoi libevent?
Poids léger
Vite-fait
Stable
Multiplateforme
Préinstallé sur la plupart des systèmes d'exploitation de type Unix prêt à l'emploi
Utilisé par de nombreux développeurs (il est plus facile de trouver des employés familiarisés avec cette technologie)
Il y a un routeur intégré (routeur)
libevent . - . "" , C++ - ( ).
, ( Valgrind).
libevent libevent-dev unix- .
, dpkg -l | grep event .
, FindLibEvent.cmake ( _/cmake_modules)
# ${LIBEVENT_INCLUDE_DIR}
find_path(LIBEVENT_INCLUDE_DIR event.h
PATHS
/usr/local
/opt
PATH_SUFFIXES
include
)
# ${LIBEVENT_LIB}
find_library(LIBEVENT_LIB
NAMES
event
PATHS
/usr/local
/opt
PATH_SUFFIXES
lib
lib64
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
LIBEVENT_LIB
LIBEVENT_INCLUDE_DIR
)
( _/imported/libevent.cmake)
find_package(LibEvent REQUIRED) # FindLibEvent.cmake
add_library(libevent STATIC IMPORTED GLOBAL) # target
# target- FindLibEvent.cmake
set_target_properties(libevent PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${LIBEVENT_INCLUDE_DIR})
# target- FindLibEvent.cmake
set_target_properties(libevent PROPERTIES IMPORTED_LOCATION ${LIBEVENT_LIB})
libevent cmake- .
, 1
target_link_libraries(${PROJECT_NAME}
PUBLIC
libevent
)
, FindLibEvent.cmake
find_package(LibEvent REQUIRED)
target_link_libraries(${PROJECT_NAME}
PUBLIC
${LIBEVENT_LIB}
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${LIBEVENT_INCLUDE_DIR}
)
HTTP ,
// , :
// *
// *
// * HTTP(, .)
#include <evhttp.h>
//
auto listener = std::make_shared<event_base, decltype(&event_base_free)>(event_base_new(), &event_base_free);
// HTTP
auto server = std::make_shared<evhttp, decltype(&evhttp_free)> (evhttp_new(listener.get()), &evhttp_free);
//
//
evhttp_set_gencb(server.get(), [](evhttp_request*, void*) {}, nullptr);
//
evhttp_set_cb (server.get(), "/my_path", [](evhttp_request*, void*) {}, nullptr);
//
return event_base_dispatch(listener.get());
Maintenant, notre serveur peut accepter les demandes, mais n'importe quel serveur doit répondre à l'application cliente. Pour cela, nous générons des réponses dans les gestionnaires.
//
auto buffer = std::make_shared<evbuffer, decltype(&evbuffer_free)>(evbuffer_new(), &evbuffer_free);
evbuffer_add(buffer, msg.c_str(), msg.length()); //
evhttp_send_reply(request, HTTP_OK, "", buffer); //
Nous avons terminé la communication à part entière sur notre serveur, parlons maintenant de l'obtention d'informations utiles à partir des demandes des clients.
La première étape consiste à analyser les paramètres GET. Ce sont les paramètres qui sont passés dans l'URI de la requête (par exemple http://www.hostname.ru ? Key = value )
struct evkeyvalq params;
evhttp_parse_query(request->uri, ¶ms); // GET
// GET-
std::string value = evhttp_find_header(¶ms, "key");
// GET-
for (auto it = params.tqh_first; it != nullptr; it = it->next.tqe_next)
std::cout << it->key << ":" << it->value << std::endl;
//
evhttp_clear_headers(¶ms);
Ensuite, vous devez obtenir le corps de la requête
auto input = request->input_buffer; //
// ,
auto length = evbuffer_get_length(input);
char* data = new char[length];
evbuffer_copyout(input, data, length); //
std::string body(data, length); //
delete[] data; //
return body;
Attention Les fonctions de rappel ne prennent pas en charge les interruptions (capture de valeurs avec des fonctions lambda), par conséquent, seuls les membres et méthodes statiques peuvent être utilisés dans les callbacks!