
Në këtë artikull, do të flas për mënyrën se si krijova një model (cookiecutter) dhe setup-in për të shkruar një shërbim REST API në C++ duke përdorur docker/docker-compose dhe menaxherin e paketave conan.
Gjatë një hackatoni tjetër, në të cilin mora pjesë si zhvillues backend, u paraqit pyetja se në çfarë gjuhë të shkruhej mikroshërbimi i ri. E gjithë puna e bërë deri në atë moment ishte realizuar nga unë dhe në gjuhën Python, pasi kolegu im ishte specialist në këtë fushë dhe merrej profesionalisht me zhvillimin e backend-eve, ndërsa unë isha zhvillues i sistemeve të integruara dhe shkruaja në të madhe e të tmerrshme C++, duke e mësuar Python vetëm në universitet.
Pra, para nesh është shtruar detyra për të shkruar një shërbim me ngarkesë të lartë, me detyrën kryesore të përpunimit të të dhënave që i dërgoheshin dhe regjistrimit të tyre në DB. Pas një pushimi, shoku im më sugjeroi si zhvillues C++ ta shkruaj këtë shërbim në plusa, duke arsyetuar se do të ishte më shpejt, më produktiv dhe gjithashtu, juria do të ishte e impresionuar nga mënyra se si dimë të menaxhojmë burimet e ekipit. Unë përgjigja se kurrë nuk kisha bërë një gjë të tillë në C++ dhe me lehtësi mund të dedikoja 20+ orët e mbetura në kërkimin, kompilimin dhe lidhjen e bibliotekave të përshtatshme. Në terma më të thjeshtë, u tremba. Në atë pikë vendosëm ta përfundojmë të gjithë punën në Python.
Tani, nĂ« kohĂ«n e vetĂ«izolimit tĂ« detyrueshĂ«m, vendosa tĂ« kuptoj si tĂ« shkruaj shĂ«rbime nĂ« C++. E para qĂ« duhej bĂ«rĂ« ishte tĂ« zgjedh njĂ« bibliotekĂ« tĂ« pĂ«rshtatshme. Zgjedhja ime ra nĂ« , pasi ishte shkruar nĂ« stilin objekt-orientuar dhe gjithashtu kishte dokumentacion tĂ« mirĂ«. Gjithashtu, u paraqit problemi i zgjedhjes sĂ« sistemit tĂ« ndĂ«rtimit. Deri nĂ« atĂ« moment kisha punuar vetĂ«m me Visual Studio, IAR dhe makefile tĂ« âpastĂ«râ. Dhe asnjĂ« nga kĂ«to sisteme nuk mĂ« tĂ«rhiqte, pasi planifikoja tĂ« niste tĂ« gjithĂ« shĂ«rbimin nĂ« njĂ« kontejner docker. AtĂ«herĂ« vendosa tĂ« provoj tĂ« kuptoj cmake dhe njĂ« menaxher paketa interesante . Ky menaxher pakete lejonte tĂ« shkruheshin tĂ« gjitha varĂ«sitĂ« nĂ« njĂ« skedar
conanfile.txt
[requires]
poco/1.9.3
libpq/11.5
[generators]
cmake
dhe me një komandë të thjeshtë «conan install .» të instalosh bibliotekat e nevojshme. Sigurisht, duhej bërë gjithashtu disa ndryshime në
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
Pas kësaj, fillova të kërkoj një bibliotekë për të punuar me PostgreSQL, sepse kisha pak përvojë me të, dhe shërbimet tona në Python bashkëvepronin me të. Dhe e dini çfarë mësova? Ajo është në POCO! Por conan nuk e di se ajo është në POCO dhe nuk di ta ndërtojë, në depo është një skedar konfigurimi i përjetshëm (unë tashmë i kam shkruar kësaj gabimi krijuesve të POCO). Prandaj, do të duhet të kërkoj një bibliotekë tjetër.
Dhe atëherë ndërsa zgjodha një bibliotekë më pak të njohur . Dhe pata një fat të madh, ajo tashmë ishte në conan dhe madje ishte ndërtuar dhe kompozuar.
Hapi tjetër ishte shkrimi i një shablloni shërbimi, i cili mund të përpunojë kërkesat.
Ne duhet ta trashëgojmë klasën tonë TemplateServerApp nga Poco::Util::ServerApplication dhe të tejkalojmë metodën 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(); // prisni për CTRL-C ose vrasin
cerr << "Shutting down..." << endl;
s.stop();
return Application::EXIT_OK;
}Në metodën main duhet të vendosim parametrat: porti, numri i thread-ëve dhe madhësia e radhës. Dhe më e rëndësishmja, duhet të vendosim trajtuesin e kërkesave hyrëse. Kjo bëhet duke krijuar një fabrikë
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};NĂ« rastin tim, ajo thjesht krijon çdo herĂ« tĂ« njĂ«jtin trajtues â TemplateServerAppHandler. PikĂ«risht kĂ«tu mund tĂ« vendosim logjikĂ«n tonĂ« tĂ« biznesit.
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();
}
};Gjithashtu, krijova një model klase për të punuar me PostgreSQL. Për të ekzekutuar një SQL të thjeshtë, si krijimi i një tabele, ekziston metoda ExecuteSQL(). Për kërkesa më komplekse ose për të marrë të dhëna, do të duhet të merrni lidhjen përmes GetConnection() dhe të përdorni API-në libpg. (Ndoshta më vonë do ta korrigjoj këtë padrejtësi).
Baza e të dhënave
#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;
};Të gjitha parametrat për lidhjen me bazën e të dhënave merren nga mjedisi, kështu që ju gjithashtu duhet të krijoni dhe konfiguroni skedarin .env
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Mund të shihni të gjithë kodin në

Dhe arriti faza e fundit e shkruarjes së dockerfile dhe docker-compose.yml. Të them të drejten, kjo mori shumicën e kohës, dhe jo vetëm sepse unë jam fillestar, e cila kërkonte të përmbledhja e librarive çdo herë, por për shkak të pengesave me conan. Për shembull, për të lejuar conan të shkarkojë, instaloje dhe ndihmojë për të ndërtuar varësitë e nevojshme, nuk mjafton të shkruash "conan install .", i nevojitet gjithashtu të kalosh parametrin -s compiler.libcxx=libstdc++11, ndryshe rrezikon të marrësh një grumbull gabimesh gjatë fazës së lidhjes së aplikacionit tuaj. Kam qëndruar me këtë gabim për disa orë dhe shpresoj se ky artikull do t'i ndihmojë të tjerët të zgjidhin këtë problem për një kohë më të shkurtër.
MĂ« pas, pas shkruarjes sĂ« docker-compose.yml, sipas kĂ«shillĂ«s sĂ« shokut tim, shtova mbĂ«shtetje pĂ«r dhe tani mund tĂ« merrni njĂ« model tĂ« plotĂ« pĂ«r shĂ«rbimin REST API nĂ« C++, me njĂ« ambient tĂ« konfiguruar, dhe PostgreSQL tĂ« ngritur, thjesht duke futur nĂ« konsolĂ«n "cookiecutter ". Pastaj, "docker-compose up âbuild".
Shpresoj se ky model do t'i ndihmojë fillestarët në rrugën e tyre të vështirë të zhvillimit të aplikacioneve REST API në gjuhën e madhe dhe të fortë, por kaq të ngadaltë, si C++.
Gjithashtu, rekomandoj shumë të lexoni këtë artikull. Në të shpjegohet më mirë sesi të punoni me POCO dhe të shkruani shërbimin tuaj REST API.
Burimi: habr.com
