Selles artiklis räägin sellest, kuidas ma lõin malli (cookiecutter) ja seadistasin C++ keeles REST API teenuse kirjutamise keskkonna, kasutades docker/docker-compose ja conan paketihaldurit.
Järgmisel häkatonil, kus taustaarendajana osalesin, tekkis küsimus, mida järgmise mikroteenuse kirjutamiseks kasutada. Kõik, mis seni kirjutatud on, on minu ja minu kirjutatud
Niisiis seisis meie ees ülesanne kirjutada suure koormusega teenus, mille põhiülesanne oli sinna saabuvate andmete eeltöötlemine ja andmebaasi kirjutamine. Ja pärast järjekordset suitsupausi soovitas sõber mul kui C++ arendajal see teenus professionaale kasutades kirjutada. Väidetavalt on see kiirem, produktiivsem ja üldiselt on žüriil hea meel, kuidas me teame, kuidas meeskonna ressursse hallata. Mille peale vastasin, et ma pole kunagi C++-s selliseid asju teinud ja saan ülejäänud 20+ tundi vabalt pühendada sobivate teekide otsimisele, koostamisele ja linkimisele. Lihtsamalt öeldes läksin tühjaks. Just selle otsustasime ja Pythonis kõik rahulikult lõpule viisime.
Nüüd, sunnitud eneseisolatsiooni ajal, otsustasin välja mõelda, kuidas C++ keeles teenuseid kirjutada. Esimese asjana tuli otsustada sobiv raamatukogu. Minu valik langes peale
conanfile.txt
[nõuab]poco/1.9.3
libpq/11.5
ja lihtsa käsuga "conan install." installige vajalikud raamatukogud. Loomulikult oli vaja ka muudatusi teha
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
Pärast seda hakkasin otsima teeki, millega PostgreSQL-iga töötada, kuna sellega oli mul vähe töökogemust ja see oli ka see, millega meie Pythoni teenused suhtlesid. Ja kas sa tead, mida ma õppisin? See on POCO-s! Kuid conan ei tea, et see on POCO-s, ega tea, kuidas seda ehitada; hoidlas on aegunud konfiguratsioonifail (sellest veast olen juba kirjutanud POCO loojatele). See tähendab, et peate otsima teise raamatukogu.
Ja siis langes minu valik vähem populaarsele raamatukogule
Järgmine samm oli teenusemalli kirjutamine, mis suudab taotlusi töödelda.
Peame pärima oma klassi TemplateServerApp Poco::Util::ServerApplicationilt ja alistama põhimeetodi.
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;
}
Põhimeetodis peame määrama parameetrid: port, lõimede arv ja järjekorra suurus. Ja mis kõige tähtsam, peate määrama sissetulevate päringute töötleja. Seda tehakse tehase loomisega
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
Minu puhul loob see lihtsalt iga kord sama töötleja – TemplateServerAppHandler. Siin saame oma äriloogika paigutada.
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();
}
};
Samuti lõin PostgreSQL-iga töötamiseks klassi malli. Lihtsa SQL-i tegemiseks, näiteks tabeli loomiseks, on olemas meetod ExecuteSQL(). Keerulisemate päringute või andmete otsimise jaoks peate looma ühenduse kaudu GetConnection() ja kasutage libpg API-t. (Ehk hiljem parandan selle ebaõigluse).
andmebaas
#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;
};
Kõik andmebaasiga ühenduse loomise parameetrid on võetud keskkonnast, seega tuleb luua ja seadistada ka env fail
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
Kogu koodi näete aadressil
Ja nüüd tuleb dockerfile'i ja docker-compose.yml kirjutamise viimane etapp. Ausalt öeldes võttis see suurema osa ajast ja mitte ainult sellepärast, et ma olen noob, sest iga kord oli vaja raamatukogusid uuesti üles ehitada, vaid ka conani lõksude tõttu. Näiteks selleks, et conan saaks alla laadida, installida ja luua vajalikud sõltuvused, ei piisa ainult "conan install ." allalaadimisest, vaid peab edastama ka parameetri -s compiler.libcxx=libstdc++11, vastasel juhul võite oma rakenduse linkimise etapis saada hunniku vigu. Olen selle veaga mitu tundi ummikus olnud ja loodan, et see artikkel aitab teistel inimestel seda probleemi lühema ajaga lahendada.
Järgmiseks, pärast docker-compose.yml kirjutamist, lisasin sõbra nõuandel tuge
Loodan, et see mall aitab algajaid nende raskel teel arendada REST API rakendusi suurepärases ja võimsas, kuid sellises kohmakas keeles nagu C++.
Samuti soovitan soojalt siit lugeda
Allikas: www.habr.com