In diesem Artikel werde ich mich auf die Verwendung von libevent in debian + gcc + cmake verlassen , aber auf anderen Unix-ähnlichen Betriebssystemen sollte es keine Schwierigkeiten geben (für Windows müssen Sie aus Quellen erstellen und FindLibEvent.cmake ändern Datei)
Vorwort
Ich habe seit ungefähr 3 Jahren Microservices entwickelt, aber ich hatte kein anfängliches Verständnis für einen geeigneten Technologie-Stack. Versuchte viele verschiedene Ansätze (von denen einige OpenDDS und Apache-Thrift waren ), entschied sich aber schließlich für RestApi .
RestApi kommuniziert über HTTP-Anforderungen, die wiederum die Datenstruktur von Headern und Anforderungskörpern darstellen, die über einen Socket übertragen werden. Das erste, was mir auffiel, war Boost / Asio, das TCP- Sockets bereitstellt, aber es gibt Schwierigkeiten mit dem Umfang der Entwicklung:
Es ist notwendig, den korrekten Datenempfang über den Socket zu schreiben
Selbstgeschriebenes Parsen von Headern
Selbstgeschriebenes Parsen von GET-Parametern
Pfadrouting
Der zweite in der Reihe war POCO (POcket COmponents), das einen übergeordneten HTTP-Server hat, aber immer noch ein Problem mit einer Reihe von selbst geschriebenen Funktionen hatte. Darüber hinaus ist dieses Tool etwas schwerer und bietet Funktionen, die möglicherweise nicht erforderlich sind (es überlastet unsere Microservices ein wenig). POCO ist auf andere Aufgaben als Microservices ausgerichtet.
Lassen Sie uns deshalb weiter über Libevent sprechen, bei dem ich aufgehört habe.
Warum libevent?
Leicht
Schnell
Stabil
Plattformübergreifend
Auf den meisten Unix-ähnlichen Betriebssystemen sofort vorinstalliert
Wird von vielen Entwicklern verwendet (es ist einfacher, Mitarbeiter zu finden, die mit dieser Technologie vertraut sind).
Es gibt einen eingebauten Router (Router)
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());
Jetzt kann unser Server Anforderungen annehmen, aber jeder Server muss auf die Clientanwendung antworten. Dazu generieren wir Antworten in den Handlern.
//
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); //
Wir haben die vollständige Kommunikation auf unserem Server abgeschlossen. Lassen Sie uns nun darüber sprechen, wie Sie nützliche Informationen aus Clientanfragen erhalten.
Der erste Schritt besteht darin, die GET-Parameter zu analysieren. Dies sind die Parameter, die in der Anforderungs-URI übergeben werden (z. B. 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);
Als Nächstes müssen Sie den Anforderungshauptteil abrufen
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;
Achtung Rückruffunktionen unterstützen keine Interrupts (Erfassen von Werten mit Lambda-Funktionen), daher können nur statische Elemente und Methoden in Rückrufen verwendet werden!