
Ebben a cikkben arról fogok beszélni, hogyan készítettem el egy sablont (cookiecutter), és hogyan állítottam be a környezetet egy C++-os REST API szolgáltatás írásához, Docker / Docker-Compose és a Conan csomagkezelő használatával.
Egy hackathon során, ahol backend fejlesztőként részt vettem, felmerült a kérdés, hogy mivel írjuk meg a következő mikroszervizt. Minden eddig írt kódot én és a Python nyelven írtunk, mivel a kollégám szakértő volt ezen a területen, és professzionálisan foglalkozott a backend fejlesztésével, míg én egyébként beágyazott rendszerek fejlesztője voltam, és a nagyszerű és borzalmas C++-t használtam, míg a Python-t csak az egyetemen tanultam.
Tehát a feladatunk az volt, hogy egy nagy terhelésre tervezett szolgáltatást írjunk, amelynek fő feladata a beérkező adatok előfeldolgozása és azok elmentése az adatbázisba volt. A következő szünetben a barátom javasolta, hogy C++ fejlesztőként én írjam ezt a szolgáltatást C++-ban. Az érvet azzal támasztotta alá, hogy így gyorsabb és hatékonyabb lesz, és a zsűri is el lesz ragadtatva, hogy milyen jól bántunk a csapat erőforrásaival. Erre én azt válaszoltam, hogy soha nem foglalkoztam hasonlókkal C++-ban, és könnyedén eltölthetem a hátralévő 20+ órát a megfelelő könyvtárak keresésével, fordításával és összekapcsolásával. Egyszerűbben fogalmazva, megijedtem. Ezen a ponton abban maradtunk, és nyugodtan befejeztük az egészet Pythonban.
Jelenleg, a kényszerű önizolálás időszakában úgy döntöttem, hogy megtanulom, hogyan lehet C++-ban szolgáltatásokat írni. Az első dolog, amit meg kellett tennem, az volt, hogy kiválasszak egy megfelelő könyvtárat. Az én választásom a , mivel ez objektumorientált stílusban készült, és jó dokumentációval büszkélkedhetett. Emellett felmerült a kérdés az építési rendszer kiválasztásáról is. Eddig csak a Visual Studio-val, IAR-rel és a „meztelen” makefile-kkal dolgoztam. Egyik rendszer sem vonzott különösebben, mivel azt terveztem, hogy az egész szolgáltatást Docker konténerben futtatom. Így döntöttem úgy, hogy próbálkozom a cmake és egy érdekes csomagkezelővel, a használatával. Ez a csomagkezelő lehetővé tette, hogy minden függőséget egy fájlban rögzítsek,
conanfile.txt
[requirements]
poco/1.9.3
libpq/11.5
[generators]
cmake
és egy egyszerű "conan install ." parancs segítségével telepítsem a szükséges könyvtárakat. Természetesen a
CMakeLists.txt-en is módosításokat kellett végezni.
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
Ezután elkezdtem keresni egy könyvtárat a PostgreSQL-hez, mert volt egy kis tapasztalatom vele, és a szolgáltatásaink is Pythonban kommunikáltak vele. És tudja, mit tudtam meg? Ott van a POCO-ban! De a conan nem tud róla, hogy létezik a POCO-ban, és nem tudja megépíteni, a repóban egy elavult konfigurációs fájl található (már írtam erről a hibáról a POCO készítőinek). Ezért másik könyvtárat kell keresnem.
Így a választásom egy kevésbé népszerű könyvtárra esett. . És hihetetlenül szerencsés voltam, mert már benne volt a conan-ban, és még össze is lehetett szerelni.
A következő lépés a kéréskezelő szolgáltatás sablonjának megírása volt.
Örökölnünk kell a TemplateServerApp osztályt a Poco::Util::ServerApplication-ból, és felül kell írni a main metódust.
TemplateServerApp
#pragma once
#include <string>
#include <vector>
#include <Poco/Util/ServerApplication.h>
class TemplateServerApp : public Poco::Util::ServerApplication
{
protected:
int main(const std::vector<std::string> &);
};int TemplateServerApp::main(const vector &)
{
HTTPServerParams* pParams = new HTTPServerParams;
pParams->setMaxQueued(100);
pParams->setMaxThreads(16);
HTTPServer s(new TemplateRequestHandlerFactory, ServerSocket(8000), pParams);
s.start();
cerr << "Server started" << endl;
waitForTerminationRequest(); // wait for CTRL-C or kill
cerr << "Shutting down..." << endl;
s.stop();
return Application::EXIT_OK;
}A main metódusban meg kell adnunk a paramétereket: port, szálak száma és a sor mérete. A legfontosabb, hogy meg kell adnunk a bejövő igények kezelőjét. Ezt úgy tesszük, hogy létrehozunk egy gyárat.
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};Az én esetemben ez egyszerűen minden alkalommal ugyanazt a kezelőt – a TemplateServerAppHandler-t – hozza létre. Itt tudjuk elhelyezni az üzleti logikánkat.
TemplateServerAppHandler
class TemplateServerAppHandler : public HTTPRequestHandler
{
public:
void handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
{
URI uri(req.getURI());
string method = req.getMethod();
cerr << "URI: " << uri.toString() << endl;
cerr << "Method: " << req.getMethod() << endl;
StringTokenizer tokenizer(uri.getPath(), "/", StringTokenizer::TOK_TRIM);
HTMLForm form(req,req.stream());
if(!method.compare("POST"))
{
cerr << "POST" << endl;
}
else if(!method.compare("PUT"))
{
cerr << "PUT" << endl;
}
else if(!method.compare("DELETE"))
{
cerr << "DELETE" << endl;
}
resp.setStatus(HTTPResponse::HTTP_OK);
resp.setContentType("application/json");
ostream& out = resp.send();
out << "{"hello":"heh"}" << endl;
out.flush();
}
};Én is készítettem egy osztály sablont a PostgreSQL használatához. Az egyszerű SQL végrehajtásához, például egy tábla létrehozásához, van egy metódus ExecuteSQL(). Bonyolultabb lekérdezésekhez vagy adatok beszerzéséhez kapcsolatot kell szereznünk a GetConnection() metódussal és a libpg API-ját kell használnunk. (Lehet, hogy ezt a jogtalanságot később orvoslom).
Adatbázis
#pragma once
#include <memory>
#include <mutex>
#include <libpq-fe.h>
class Database
{
public:
Database();
std::shared_ptr<PGconn> GetConnection() const;
bool ExecuteSQL(const std::string& sql);
private:
void establish_connection();
void LoadEnvVariables();
std::string m_dbhost;
int m_dbport;
std::string m_dbname;
std::string m_dbuser;
std::string m_dbpass;
std::shared_ptr<PGconn> m_connection;
};Minden adatbázis-kapcsolati paraméter a környezetből származik, ezért neked is létre kell hoznod és be kell állítanod egy .env fájlt
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Megnézheted a teljes kódot a

Elérkeztünk a Dockerfile és a docker-compose.yml írásának utolsó szakaszához. Őszintén szólva, ennek megírása sok időt vett igénybe, nemcsak azért, mert új vagyok, és minden alkalommal újra kellett építeni a könyvtárakat, hanem a conan problémái miatt is. Például ahhoz, hogy a conan letöltse, telepítse és lebuildelje a szükséges függőségeket, nem elég, ha kiadod a „conan install .” parancsot, hanem a -s compiler.libcxx=libstdc++11 paramétert is meg kell adnod, különben rengeteg hibaüzenetet kapsz az alkalmazásod összesítésekor. Ezzel a hibával órákat ültem, és remélem, hogy ez a cikk segít másoknak, hogy ezt a problémát gyorsabban megoldják.
Ezután, a docker-compose.yml megírása után, a barátom tanácsára hozzáadtam a támogatást és most teljes sablont kaphatsz egy C++ REST API szolgáltatáshoz, beállított környezettel és működő PostgreSQL-lal, csupán annyit kell beírnod a konzolba: „cookiecutter ”. Aztán „docker-compose up —build”.
Remélem, hogy ez a sablon segíteni fog a kezdőknek a REST API alkalmazások fejlesztésének nehéz útján a nagyszerű és hatalmas, de oly lassú nyelven, mint a C++.
Ezen túlmenően, nagyon ajánlom, hogy olvasd el ezt a cikket. Benne részletesebben magyarázzák el, hogyan kell dolgozni a POCO-val és hogyan írhatod meg a saját REST API szolgáltatásodat.
Forrás: habr.com
