
În acest articol voi vorbi despre cum am creat un șablon (cookiecutter) și am configurat un mediu pentru scrierea unui serviciu API REST în C++ folosind docker/docker-compose și managerul de pachete conan.
În timpul următorului hackathon, la care am participat ca dezvoltator backend, a apărut întrebarea despre ce să folosim pentru a scrie următorul microserviciu. Tot ce s-a scris până acum a fost scris de mine și de mine în Python, din moment ce colegul meu era un expert în acest domeniu și dezvoltase backend-uri profesional, în timp ce eu eram în general un dezvoltator de sisteme încorporate și scriam în C++ grozav și groaznic, și tocmai am învățat Python la universitate.
Așadar, ne-am confruntat cu sarcina de a scrie un serviciu cu încărcare mare, a cărui sarcină principală era să preprocesăm datele care vin la acesta și să le scriem în baza de date. Și după încă o pauză de fum, un prieten mi-a sugerat ca eu, ca dezvoltator C++, să scriu acest serviciu folosind profesioniști. Argumentând acest lucru este că va fi mai rapid, mai productiv și, în general, juriul va fi încântat de modul în care știm să gestionăm resursele echipei. La care i-am răspuns că nu am făcut niciodată astfel de lucruri în C++ și că aș putea dedica cu ușurință cele peste 20 de ore rămase căutării, compilării și conectării bibliotecilor adecvate. Pur și simplu, m-am rătăcit. Asta am decis și am finalizat totul cu calm în Python.
Acum, în timpul autoizolării forțate, am decis să îmi dau seama cum să scriu servicii în C++. Primul lucru de făcut a fost să decideți asupra unei biblioteci potrivite. Alegerea mea a căzut , deoarece a fost scris într-un stil orientat pe obiecte și se lăuda și cu documentație normală. De asemenea, a apărut și întrebarea despre alegerea unui sistem de asamblare. Până în acest moment am lucrat doar cu Visual Studio, IAR și makefile-uri goale. Și niciunul dintre aceste sisteme nu m-a atras, deoarece am plănuit să rulez întregul serviciu într-un container docker. Apoi am decis să încerc să descopăr cmake și un manager de pachete interesant . Acest manager de pachete v-a permis să înregistrați toate dependențele într-un singur fișier
conanfile.txt
[necesită]
poco/1.9.3
libpq/11.5
generatoare
cmake
și cu o comandă simplă „conan install ”. instalați bibliotecile necesare. Desigur, a fost necesar să se facă și modificări
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
După aceea, am început să caut o bibliotecă pentru a lucra cu PostgreSQL, deoarece era cea cu care aveam puțină experiență de lucru și era și cea cu care interacționau serviciile noastre Python. Și știi ce am învățat? Este în POCO! Dar conan nu știe că este în POCO și nu știe cum să-l construiască există un fișier de configurare învechit în depozit (am scris deja despre această eroare creatorilor POCO); Aceasta înseamnă că va trebui să căutați o altă bibliotecă.
Și apoi alegerea mea a căzut pe o bibliotecă mai puțin populară . Și am fost incredibil de norocos, era deja în conan și chiar era asamblat și asamblat.
Următorul pas a fost să scrieți un șablon de serviciu care poate procesa cereri.
Trebuie să moștenim clasa TemplateServerApp de la Poco::Util::ServerApplication și să suprascriem metoda principală.
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<string> &)
{
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;
}În metoda principală trebuie să setăm parametrii: portul, numărul de fire și dimensiunea cozii. Și cel mai important, trebuie să specificați un handler pentru cererile primite. Acest lucru se realizează prin crearea unei fabrici
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};În cazul meu, pur și simplu creează același handler de fiecare dată - TemplateServerAppHandler. Aici ne putem plasa logica de afaceri.
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();
}
};De asemenea, am creat un șablon de clasă pentru a lucra cu PostgreSQL. Pentru a efectua SQL simplu, cum ar fi crearea unui tabel, există o metodă ExecuteSQL(). Pentru interogări mai complexe sau pentru preluarea datelor, va trebui să obțineți o conexiune prin GetConnection() și utilizați API-ul libpg. (Poate că mai târziu voi corecta această nedreptate).
Baza de date
#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;
};Toți parametrii pentru conectarea la baza de date sunt preluați din mediu, așa că trebuie să creați și să configurați și fișierul .env
.și V
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Puteți vedea tot codul la

Și acum vine etapa finală a scrierii fișierului docker și docker-compose.yml. Sincer să fiu, asta a durat cea mai mare parte a timpului, și nu numai pentru că sunt un noob, pentru că a fost necesar să reconstruiesc bibliotecile de fiecare dată, ci din cauza capcanelor lui Conan. De exemplu, pentru ca conan să descarce, să instaleze și să construiască dependențele necesare, nu este suficient să descarce „conan install .”, de asemenea, trebuie să treacă parametrul -s compiler.libcxx=libstdc++11, în caz contrar riscați să primiți o grămadă de erori în etapa de conectare a aplicației dvs. Am fost blocat cu această eroare de câteva ore și sper că acest articol va ajuta alți oameni să rezolve această problemă în mai puțin timp.
În continuare, după ce am scris docker-compose.yml, la sfatul prietenului meu, am adăugat suport și acum vă puteți obține un șablon cu drepturi depline pentru un serviciu API REST în C++, cu un mediu personalizat și PostgreSQL instalat, pur și simplu introducând „cookiecutter” în consolă " Și apoi „docker-compose up —build”.
Sper că acest șablon îi va ajuta pe începători pe calea lor dificilă de dezvoltare a aplicațiilor REST API într-un limbaj minunat și puternic, dar atât de stângaci precum C++.
De asemenea, vă recomand să citiți aici articol. Acesta explică mai detaliat cum să lucrați cu POCO și să scrieți propriul serviciu API REST.
Sursa: www.habr.com
