Cómo empezar a escribir un microservicio en C ++

En este artículo, confiaré en el uso de libevent dentro de debian + gcc + cmake , pero en otros sistemas operativos similares a Unix, no deberían surgir dificultades (para Windows, necesitará compilar a partir de fuentes y refinar el archivo FindLibEvent.cmake )





Prefacio

He estado desarrollando microservicios durante aproximadamente 3 años, pero no tenía un conocimiento inicial de una pila de tecnología adecuada. Probé muchos enfoques diferentes (algunos de los cuales fueron OpenDDS y apache- thrift ) pero terminé decidiéndome por RestApi .





RestApi se comunica a través de solicitudes HTTP, que a su vez representan la estructura de datos de los encabezados y cuerpos de solicitud transmitidos a través de un socket. Lo primero que noté fue boost / asio, que proporciona sockets tcp, pero hay dificultades con la cantidad de desarrollo:





  • Es necesario escribir la recepción correcta de datos en el zócalo.





  • Análisis de encabezado autoescrito





  • Análisis autoescrito de parámetros GET





  • Ruta de ruta





El segundo en la línea fue POCO (POcket COmponents), que tiene un servidor HTTP de nivel superior, pero todavía tenía un problema con un montón de funciones autoescritas. Además, esta herramienta es un poco más pesada y proporciona una funcionalidad que puede no ser necesaria (sobrecarga un poco nuestros microservicios). POCO está orientado a otras tareas además de los microservicios.





Por lo tanto, más adelante hablemos de libevent en el que me detuve.





¿Por qué libevent?

  • Ligero





  • Rápido





  • Estable





  • Multiplataforma





  • Preinstalado en la mayoría de los sistemas operativos tipo Unix listos para usar





  • Utilizado por muchos desarrolladores (es más fácil encontrar empleados que estén familiarizados con esta tecnología)





  • Hay un enrutador incorporado (enrutador)





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());
      
      



Ahora nuestro servidor puede aceptar solicitudes, pero cualquier servidor debe responder a la aplicación del cliente. Para ello, generamos respuestas en los manejadores.





//     
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); //  
      
      



Hemos completado la comunicación completa en nuestro servidor, ahora hablemos sobre cómo obtener información útil de las solicitudes de los clientes.





El primer paso es analizar los parámetros GET. Estos son los parámetros que se pasan en el URI de la solicitud (por ejemplo, http://www.hostname.ru ? Key = value )





struct evkeyvalq params;
evhttp_parse_query(request->uri, &params); //  GET 

//      GET-   
std::string value = evhttp_find_header(&params, "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(&params);
      
      



A continuación, debe obtener el cuerpo de la solicitud.





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;
      
      



Atención Las funciones de devolución de llamada no admiten interrupciones (captura de valores con funciones lambda), por lo tanto, solo los miembros y métodos estáticos se pueden usar dentro de las devoluciones de llamada.








All Articles