
U ovom članku ću govoriti o tome kako sam stvorio predložak (cookiecutter) i postavio okruženje za pisanje REST API servisa na C++ koristeći docker/docker-compose i paketni upravitelj conan.
Tijekom još jednog hackathona, na kojem sam sudjelovao kao backend programer, postavilo se pitanje na čemu napisati novi mikroservis. Sve što je do sada napisano, pisali smo moj kolega i ja na jeziku Python, budući da je moj kolega bio stručnjak u tom području i profesionalno se bavio razvojem backend-a, dok sam ja zapravo bio programer za ugrađene sustave i pisao na velikom i strašnom C++, dok sam Python samo malo naučio na sveučilištu.
Dakle, pred nama se postavio zadatak napisati visoko opterećen servis, čija je glavna svrha bila preprocesiranje dolaznih podataka i njihovo zapisivanje u BBD. I nakon još jedne pauze, prijatelj mi je predložio, kao C++ programeru, da napišem ovaj servis na plusima. Obrazlažući to time da će tako biti brže, učinkovitije, a i juri će biti oduševljeni kako znamo raspolagati resursima tima. Na što sam odgovorio da nikada nisam radio takve stvari na C++ i da se bez problema mogu preostalih 20+ sati posvetiti pronalaženju, kompilaciji i povezivanju odgovarajućih biblioteka. Ukratko, prepao sam se. Tako smo odlučili i mirno dovršili sve na Pythonu.
Sada, tijekom prisilne samoizolacije, odlučio sam se razmisliti o tome kako pisati servise na C++. Prvo što sam trebao učiniti je odabrati odgovarajuću biblioteku. Moj izbor pao je na , budući da je bila napisana u objektno orijentiranom stilu i također se mogla pohvaliti dobrom dokumentacijom. Također, postavilo se pitanje odabira sustava izrade. Do tog trenutka radio sam samo s Visual Studiom, IAR-om i "golim" makefile-om. I nijedan od tih sustava nije me privlačio, budući da sam planirao pokrenuti cijeli servis u docker kontejneru. Tada sam odlučio pokušati razumjeti cmake i zanimljiv paketni upravitelj . Ovaj paketni upravitelj omogućio je definiranje svih zavisnosti u jednoj datoteci
conanfile.txt
[requires]
poco/1.9.3
libpq/11.5
[generators]
cmake
i pomoću jednostavne naredbe „conan install .“ instalirati potrebne biblioteke. Naravno, također je bilo potrebno unijeti izmjene u
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
Nakon toga, počeo sam tražiti biblioteku za rad s PostgreSQL, jer sam imao malo iskustva s njom, a naši servisi na Pythonu su se također s njom povezivali. I znate li što sam saznao? Ona je u POCO-u! No, conan ne zna da ona postoji u POCO-u i ne zna kako je izgraditi, u repozitoriju se nalazi zastarjeli konfiguracijski datoteka (već sam o toj grešci obavijestio kreatore POCO-a). To znači da ćemo morati pronaći drugu biblioteku.
I tada je moj izbor pao na manje popularnu biblioteku . I neizmjerno sam imao sreće, jer je već bila u conan-u i čak se gradila i povezivala.
Sljedeći korak bio je pisanje predloška servisa koji može obraditi zahtjeve.
Moramo naslijediti našu klasu TemplateServerApp od Poco::Util::ServerApplication i override-ati metodu main.
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;
}U metodi main trebamo postaviti parametre: port, broj niti i veličinu reda. A najvažnije, trebamo postaviti obrtnika ulaznih zahtjeva. To se radi stvaranjem fabrike
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};U mom slučaju ona jednostavno svaki put stvara isti obrtnik — TemplateServerAppHandler. Točno ovdje možemo smjestiti našu poslovnu logiku.
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();
}
};Također sam stvorio šablon klase za rad s PostgreSQL-om. Da biste izvršili jednostavni SQL, kao što je stvaranje tablice, postoji metoda ExecuteSQL(). Za složenije upite ili dobivanje podataka morat ćete dobiti konekciju putem GetConnection() i koristiti API libpg. (Možda ću kasnije ispraviti ovu nepravdu).
Baza podataka
#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;
};Svi parametri za povezivanje s bazom podataka uzimaju se iz okruženja, tako da morate stvoriti i konfigurirati datoteku .env
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Možete pogledati sav kod na

I došao je posljednji korak pisanja dockerfile i docker-compose.yml. Iskreno, ovo je oduzelo najveći dio vremena, i ne samo zato što sam početnik, koji je svaki put morao ponovno graditi biblioteke, već zbog zamki conana. Naime, da bi conan preuzeo, instalirao i izgradio potrebne ovisnosti, nije dovoljno samo pokrenuti „conan install .“, također morate proslijediti parametar -s compiler.libcxx=libstdc++11, inače rizikujete dobiti hrpu grešaka tijekom povezivanja vaše aplikacije. Proveo sam nekoliko sati s ovom greškom i nadam se da će ovaj članak pomoći drugim ljudima da riješe ovaj problem u kraćem vremenu.
Nakon pisanja docker-compose.yml, prema savjetu svog prijatelja dodao sam podršku i sada možete dobiti puni šablon za REST API servis na C++, s konfiguriranim okruženjem i podignutim PostgreSQL-om, jednostavno upisivanjem u konzolu „cookiecutter “. A zatim „docker-compose up —build“.
Nadam se da će ovaj šablon pomoći novajlijama na njihovom teškom putu razvoja REST API aplikacija na velikom i moćnom, ali tako nespretnom jeziku kao što je C++.
Također, jako preporučujem da pročitate ovaj članak. U njemu se detaljnije objašnjava kako raditi s POCO i kako napisati svoj REST API servis.
Izvor: habr.com
