Šiame straipsnyje kalbėsiu apie tai, kaip sukūriau šabloną (cookiecutter) ir sukūriau aplinką REST API paslaugai rašyti C++ naudojant docker/docker-compose ir conan paketų tvarkyklę.
Per kitą hakatoną, kuriame dalyvavau kaip backend kūrėjas, iškilo klausimas, ką naudoti norint parašyti kitą mikroservisą. Viskas, kas buvo parašyta iki šiol, parašyta aš ir mano
Taigi, susidūrėme su užduotimi parašyti didelės apkrovos paslaugą, kurios pagrindinė užduotis buvo iš anksto apdoroti į ją ateinančius duomenis ir įrašyti į duomenų bazę. Ir po dar vienos dūmų pertraukos draugas pasiūlė man, kaip C++ kūrėjui, parašyti šią paslaugą pasinaudojant profesionalais. Teigiama, kad tai bus greitesnė, produktyvesnė ir apskritai žiuri džiaugsis, kaip mes žinome, kaip valdyti komandos išteklius. Į ką aš atsakiau, kad niekada nedariau tokių dalykų C++ ir galiu nesunkiai skirti likusias 20+ valandų tinkamų bibliotekų paieškai, kompiliavimui ir susiejimui. Paprasčiau tariant, aš nusiminiau. Taip nusprendėme ir viską ramiai užbaigėme Python.
Dabar, priverstinės saviizoliacijos metu, nusprendžiau sugalvoti, kaip rašyti paslaugas C++ kalba. Pirmas dalykas, kurį reikėjo padaryti, buvo nuspręsti dėl tinkamos bibliotekos. Mano pasirinkimas krito
conanfile.txt
[reikia]poco/1.9.3
libpq/11.5
ir su paprasta komanda "conan install". įdiegti reikiamas bibliotekas. Natūralu, kad taip pat reikėjo keisti
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
Po to pradėjau ieškoti bibliotekos, kad galėčiau dirbti su PostgreSQL, nes su ja turėjau mažai patirties ir su ja bendravo mūsų Python paslaugos. Ir ar žinai, ko aš išmokau? Tai POCO! Bet conan nežino, kad jis yra POCO, ir nežino, kaip jį sukurti; saugykloje yra pasenęs konfigūracijos failas (apie šią klaidą jau rašiau POCO kūrėjams). Tai reiškia, kad turėsite ieškoti kitos bibliotekos.
Ir tada mano pasirinkimas krito į mažiau populiarią biblioteką
Kitas žingsnis buvo parašyti paslaugos šabloną, galintį apdoroti užklausas.
Turime paveldėti savo TemplateServerApp klasę iš Poco::Util::ServerApplication ir nepaisyti pagrindinio metodo.
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;
}
Pagrindiniame metode turime nustatyti parametrus: prievadą, gijų skaičių ir eilės dydį. Ir, svarbiausia, turite nurodyti gaunamų užklausų tvarkytoją. Tai daroma kuriant gamyklą
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
Mano atveju jis tiesiog kiekvieną kartą sukuria tą patį tvarkyklę – TemplateServerAppHandler. Čia galime išdėstyti savo verslo logiką.
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();
}
};
Taip pat sukūriau klasės šabloną darbui su PostgreSQL. Norint atlikti paprastą SQL, pavyzdžiui, sukurti lentelę, yra metodas VykdytiSQL(). Norėdami atlikti sudėtingesnes užklausas ar gauti duomenis, turėsite užmegzti ryšį per Gauti ryšį () ir naudokite libpg API. (Gal vėliau šią neteisybę ištaisysiu).
duomenų bazė
#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;
};
Visi prisijungimo prie duomenų bazės parametrai yra paimti iš aplinkos, todėl taip pat reikia sukurti ir sukonfigūruoti .env failą
.NS
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
Visą kodą galite pamatyti adresu
Ir dabar ateina paskutinis dockerfile ir docker-compose.yml rašymo etapas. Tiesą sakant, tai užtrukdavo didžiąją laiko dalį, ir ne tik todėl, kad esu niekšas, nes kiekvieną kartą reikėjo atkurti bibliotekas, bet ir dėl „conan“ spąstų. Pavyzdžiui, norint, kad „conan“ galėtų atsisiųsti, įdiegti ir sukurti reikiamas priklausomybes, neužtenka atsisiųsti „conan install .“, jis taip pat turi perduoti parametrą -s compiler.libcxx=libstdc++11, kitu atveju rizikuojate gauti daugybę klaidų susiedami savo programą. Kelias valandas įstrigo ši klaida ir tikiuosi, kad šis straipsnis padės kitiems žmonėms išspręsti šią problemą per trumpesnį laiką.
Toliau, parašęs docker-compose.yml, draugo patarimu, pridėjau palaikymą
Tikiuosi, kad šis šablonas padės pradedantiesiems sudėtingame kelyje kuriant REST API programas puikia ir galinga, bet tokia gremėzdiška kalba kaip C++.
Be to, labai rekomenduoju paskaityti čia
Šaltinis: www.habr.com