Cara mulai menulis layanan mikro di C ++

Dalam artikel ini, saya akan mengandalkan penggunaan libevent dalam debian + gcc + cmake , tetapi pada sistem operasi mirip unix lainnya, seharusnya tidak ada kesulitan (untuk windows, Anda perlu membangun dari sumber dan memodifikasi FindLibEvent.cmake mengajukan)





Kata pengantar

Saya telah mengembangkan layanan mikro selama sekitar 3 tahun, tetapi saya tidak memiliki pemahaman awal tentang tumpukan teknologi yang sesuai. Mencoba banyak pendekatan berbeda (beberapa di antaranya adalah OpenDDS dan apache- thrift ) tetapi akhirnya memilih RestApi .





RestApi berkomunikasi melalui permintaan HTTP, yang pada gilirannya mewakili struktur data header dan badan permintaan yang dikirimkan melalui soket. Hal pertama yang saya perhatikan adalah boost / asio, yang menyediakan soket tcp, tetapi ada kesulitan dengan jumlah pengembangan:





  • Anda perlu menulis penerimaan data yang benar melalui soket





  • Penguraian header yang ditulis sendiri





  • Penguraian parameter GET yang ditulis sendiri





  • Perutean jalur





Baris kedua adalah POCO (POcket COmponents), yang memiliki server HTTP level lebih tinggi, tetapi masih memiliki masalah dengan banyak fungsi yang ditulis sendiri. Selain itu, alat ini sedikit lebih berat dan menyediakan fungsionalitas yang mungkin tidak diperlukan (ini sedikit membebani layanan mikro kami). POCO diarahkan untuk tugas lain selain layanan mikro.





Oleh karena itu, selanjutnya mari kita bicara tentang libevent yang saya hentikan.





Mengapa libevent?

  • Ringan





  • Cepat





  • Stabil





  • Lintas platform





  • Telah diinstal sebelumnya pada sebagian besar OS yang mirip unix di luar kotak





  • Digunakan oleh banyak pengembang (lebih mudah menemukan karyawan yang akrab dengan teknologi ini)





  • Ada built-in 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());
      
      



Sekarang server kami dapat menerima permintaan, tetapi server mana pun harus merespons aplikasi klien. Untuk ini, kami menghasilkan tanggapan di penangan.





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



Kami telah menyelesaikan komunikasi lengkap di server kami, sekarang mari kita bicara tentang mendapatkan informasi yang berguna dari permintaan klien.





Langkah pertama adalah mengurai parameter GET. Ini adalah parameter yang diteruskan dalam URI permintaan (misalnya 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);
      
      



Selanjutnya, Anda perlu mendapatkan isi permintaan





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;
      
      



Fungsi Attention Callback tidak mendukung interupsi (menangkap nilai dengan fungsi lambda), oleh karena itu, hanya anggota dan metode statis yang dapat digunakan di dalam callback!








All Articles