この記事では、debian + gcc + cmake内でのlibeventの使用に依存しますが、他のUNIXライクなオペレーティングシステムでは、問題はないはずです(Windowsの場合、ソースからビルドしてFindLibEvent.cmakeを変更する必要があります)ファイル)
序文
私は約3年間マイクロサービスを開発してきましたが、適切なテクノロジースタックについて最初は理解していませんでした。多くの異なるアプローチ(そのうちのいくつかはOpenDDSとapache-thrift)を試しましたが、最終的にRestApiに落ち着きました。
RestApiはHTTPリクエストを介して通信します。これは、ソケットを介して送信されるヘッダーとリクエストボディのデータ構造を表します。私が最初に気付いたのは、 tcpソケットを提供するboost / asioでしたが、開発の量に問題があります。
ソケットを介してデータの正しい受信を書き込む必要があります
自作のヘッダー解析
GETパラメータの自作解析
パスルーティング
2番目に並んだのはPOCO(POcket COmponents)で、これは高レベルのHTTPサーバーを備えていますが、それでも多くの自作機能に問題がありました。さらに、このツールはもう少し重量があり、必要とされない可能性のある機能を提供します(マイクロサービスが少し過負荷になります)。POCOは、マイクロサービス以外のタスクを対象としています。
したがって、私が停止したlibeventについてさらに話しましょう。
なぜlibevent?
軽量
クイック
安定している
クロスプラットフォーム
箱から出してほとんどのunixライクなOSにプリインストールされています
多くの開発者によって使用されています(このテクノロジーに精通している従業員を見つけるのは簡単です)
内蔵ルーター(ルーター)があります
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());
これで、サーバーはリクエストを受け入れることができますが、どのサーバーもクライアントアプリケーションに応答する必要があります。このために、ハンドラーで応答を生成します。
//
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); //
サーバーでの本格的な通信が完了しました。次に、クライアントの要求から有用な情報を取得する方法について説明します。
最初のステップは、GETパラメーターを解析することです。これらは、リクエストURIで渡されるパラメーターです(たとえば、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);
次に、リクエスト本文を取得する必要があります
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;
注意コールバック関数は割り込み(ラムダ関数で値をキャプチャする)をサポートしていないため、コールバック内で使用できるのは静的メンバーとメソッドのみです!