
I denne artikel vil jeg fortælle om, hvordan jeg oprettede en skabelon (cookiecutter) og indstillede miljøet til at skrive en REST API-service i C++ ved hjælp af docker/docker-compose og pakkehåndtereren conan.
Under et hackathon, hvor jeg deltog som backend-udvikler, opstod spørgsmålet om, hvad jeg skulle skrive den næste mikrotjeneste i. Alt, hvad der var skrevet indtil nu, var skrevet af mig og min på Python, da min kollega var specialist på dette område og professionelt beskæftigede sig med backend-udvikling, mens jeg egentlig var udvikler til indlejrede systemer og skrev på det store og frygtelige C++, og jeg havde kun lært Python i universitetet.
Så spørgsmålet var, hvordan vi skulle skrive en højtydende service, hvis hovedopgave var at forbehandle de indkommende data og skrive dem til databasen. Efter en pause foreslog min ven mig som C++-udvikler at skrive denne service i C++. Han argumenterede for, at det ville være hurtigere, mere effektivt, og at juryen ville være begejstret for, hvordan vi kunne håndtere teamressourcerne. Jeg svarede, at jeg aldrig havde arbejdet med sådanne ting i C++ og uden problemer kunne bruge de resterende 20+ timer på at finde, kompilere og koble de relevante biblioteker. Kort sagt, jeg blev skræmt. Det blev vi enige om, og så fortsatte vi med at skrive alt i Python.
Nu, under den tvungne selvisolation, besluttede jeg mig for at finde ud af, hvordan man skriver tjenester i C++. Det første, jeg skulle gøre, var at vælge et passende bibliotek. Mit valg faldt på , da det var skrevet i objektorienteret stil og også havde en ordentlig dokumentation. Så opstod spørgsmålet om valg af build-system. Indtil dette tidspunkt havde jeg kun arbejdet med Visual Studio, IAR og 'bare' makefiles. Og ingen af disse systemer appellerede til mig, da jeg planlagde at køre hele servicen i en docker-container. Derfor besluttede jeg at forsøge at forstå cmake og den interessante pakkehåndterer . Denne pakkehåndterer gjorde det muligt at angive alle afhængigheder i én fil
conanfile.txt
[requires]
poco/1.9.3
libpq/11.5
[generators]
cmake
og med simple kommandoen 'conan install .' installere de nødvendige biblioteker. Selvfølgelig var det også nødvendigt at foretage ændringer i
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
Efter dette begyndte jeg at lede efter et bibliotek til arbejde med PostgreSQL, da jeg havde en smule erfaring med det, og vores tjenester på Python interagerede netop med dette. Og ved du, hvad jeg fandt ud af? Det findes i POCO! Men conan ved ikke, at det findes i POCO og kan ikke bygge det; der ligger en forældet konfigurationsfil i depotet (jeg har allerede informeret POCOs skabere om denne fejl). Så det bliver nødvendigt at finde et andet bibliotek.
Og så faldt mit valg på et mindre populært bibliotek . Og jeg var utroligt heldig, det var allerede i conan og endda kunne bygges og bindes.
Næste skridt var at skrive en skabelon til en tjeneste, der kunne behandle anmodninger.
Vi skal arve vores klasse TemplateServerApp fra Poco::Util::ServerApplication og overskrive metoden 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 startet" << endl;
waitForTerminationRequest(); // vent på CTRL-C eller kill
cerr << "Lukker ned..." << endl;
s.stop();
return Application::EXIT_OK;
}I metoden main skal vi angive parametre: port, antal tråde og køstørrelse. Og det vigtigste er, at vi skal angive en behandler til indkommende anmodninger. Dette gøres ved at oprette en fabrik
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};I mit tilfælde opretter den bare konstant den samme behandler — TemplateServerAppHandler. Her kan vi placere vores forretningslogik.
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 << "Metode: " << 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();
}
};Jeg har også lavet en klasse-skabelon til at arbejde med PostgreSQL. For at udføre en simpel SQL-kommando, såsom at oprette en tabel, findes der en metode ExecuteSQL(). For mere komplekse forespørgsler eller dataindhentning skal du få forbindelsen via GetConnection() og bruge API'et libpg. (Jeg vil måske senere rette denne uretfærdighed).
Database
#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;
};Alle parametre til opkobling til databasen hentes fra miljøet, så du skal også oprette og konfigurere en .env-fil
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Du kan se hele koden på

Og nu er vi nået til det sidste trin i skrivningen af dockerfile og docker-compose.yml. Jeg må være ærlig, det tog en stor del af tiden, ikke kun fordi jeg er en nybegynder, der konstant skal genkompilere biblioteket, men også på grund af faldgruberne med conan. For eksempel, for at conan kan hente, installere og bygge de nødvendige afhængigheder, er det ikke nok at køre "conan install ."; du skal også angive parameteren -s compiler.libcxx=libstdc++11, ellers risikerer du at få en masse fejl under linkningsfasen af din applikation. Jeg brugte flere timer på denne fejl og håber, at denne artikel vil hjælpe andre med at løse dette problem hurtigere.
Derefter, efter at have skrevet docker-compose.yml, tilføjede jeg på råd fra en ven støtte til og nu kan du få en fuld skabelon til en REST API-tjeneste i C++, med et konfigureret miljø og en kørende PostgreSQL, blot ved at skrive "cookiecutter " i konsollen. Og derefter "docker-compose up —build".
Jeg håber, at denne skabelon vil hjælpe nybegyndere på deres svære vej til at udvikle REST API-applikationer i det store og mægtige, men så klodsede sprog, som C++ er.
Jeg anbefaler også kraftigt at læse denne artikel. Den forklarer detaljerne om, hvordan man arbejder med POCO og opretter sin egen REST API-tjeneste.
Kilde: habr.com
