Neste artigo, vou contar com o uso de libevent dentro do debian + gcc + cmake , mas em outros sistemas operacionais tipo unix, não deve haver dificuldades (para Windows, você precisará compilar a partir dos fontes e modificar o FindLibEvent.cmake Arquivo)
Prefácio
Tenho desenvolvido microsserviços há cerca de 3 anos, mas não tinha um entendimento inicial de uma pilha de tecnologia adequada. Tentei muitas abordagens diferentes (algumas das quais eram OpenDDS e apache-thrift ), mas acabei optando pelo RestApi .
RestApi se comunica por meio de solicitações HTTP, que por sua vez representam a estrutura de dados de cabeçalhos e corpos de solicitação transmitidos por meio de um soquete. A primeira coisa que notei foi boost / asio, que fornece sockets tcp, mas há dificuldades com a quantidade de desenvolvimento:
É necessário escrever a recepção correta de dados no soquete
Análise de cabeçalho autoescrita
Análise autoescrita de parâmetros GET
Roteamento de caminho
O segundo na fila era POCO (POcket COmponents), que tem um servidor HTTP de nível superior, mas ainda tinha um problema com várias funcionalidades de escrita própria. Além disso, essa ferramenta é um pouco mais pesada e fornece funcionalidades que podem não ser necessárias (sobrecarrega um pouco nossos microsserviços). O POCO é voltado para outras tarefas além dos microsserviços.
Portanto, vamos falar mais sobre o libevent no qual parei.
Por que libevent?
Leve
Rápido
Estábulo
Plataforma cruzada
Pré-instalado na maioria dos sistemas operacionais tipo Unix fora da caixa
Usado por muitos desenvolvedores (é mais fácil encontrar funcionários familiarizados com esta tecnologia)
Há um roteador embutido (roteador)
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());
Agora nosso servidor pode aceitar solicitações, mas qualquer servidor deve responder ao aplicativo cliente. Para isso, geramos respostas nos manipuladores.
//
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); //
Concluímos a comunicação completa em nosso servidor, agora vamos falar sobre como obter informações úteis de solicitações de clientes.
A primeira etapa é analisar os parâmetros GET. Estes são os parâmetros que são transmitidos no URI de solicitação (por exemplo 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);
Em seguida, você precisa obter o corpo da solicitação
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;
Atenção As funções de retorno de chamada não oferecem suporte a interrupções (captura de valores com funções lambda), portanto, apenas membros e métodos estáticos podem ser usados em retornos de chamada!