Mikroshërbime në C++. Trillim apo realitet?

Mikroshërbime në C++. Trillim apo realitet?

Në këtë artikull do të flas për mënyrën si krijova një template (cookiecutter) dhe konfigurita ambientin për zhvillimin e një shërbimi REST API në C++ duke përdorur docker/docker-compose dhe menaxherin e paketave conan.

Gjatë një hackathoni tjetër, ku unë merrja pjesë si zhvillues backend, u shtrua çështja se mbi çfarë do të shkruhej mikrosherbimi i radhës. Gjithçka që ishte shkruar deri në atë moment, ishte shkruar nga unë dhe miku im shokun tim 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 në thelb një zhvillues për sisteme të integruara dhe shkruaja në të madhen dhe të tmerrshmen C++, e kurse Python e kisha mësuar pak në universitet.

Pra, përpara nesh doli 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 vijnë dhe ruajtjen e tyre në DB. Dhe pas një pauze të zakonshme, shoku më sugjeroi, si zhvillues C++, që ta shkruaja këtë shërbim në C++. Duke argumentuar se do të ishte më shpejt, më produktiv, dhe gjithashtu, juria do të ishte e impresionuar nga aftësitë tona për të menaxhuar burimet e ekipit. Për këtë, unë u përgjigja se nuk kisha bërë kurrë diçka të tillë në C++ dhe mund të kaloja lehtësisht 20+ orët e mbetura duke kërkuar, kompajluar dhe lidhur libraritë e përshtatshme. Në thelb, u tremba. Kështu u vendos dhe përfundimisht e shkruam gjithçka në Python.

Tani, gjatë izolimit të detyruar, vendosa të merrem me atë se si të shkruaja shërbime në C++. E para që duhej bërë ishte të përcaktoja një bibliotekë të përshtatshme. Zgjedhja ime ra mbi POCO, pasi ajo ishte shkruar në stilin objekt-orientues dhe gjithashtu kishte dokumentacion të mirë. Gjithashtu, u shtrua çështja e zgjedhjes së sistemit të ndërtimit. Deri në atë moment kisha punuar vetëm me Visual Studio, IAR dhe "makefile" të pastër. Asnjë nga këto sisteme nuk më entuziazmonte, pasi planifikoja të aktivizoja të gjithë shërbimin në një kontejner docker. Atëherë vendosa të përpiqem të kuptoj cmake dhe një menaxher paketash interesant, conan. Ky menaxher paketash lejonte të shkruaja të gjitha varësitë në një skedë të vetme

conanfile.txt
[requires]
poco/1.9.3
libpq/11.5

[generators]
cmake

dhe me një komandë të thjeshtë "conan install ." të instaloj libraritë e nevojshme. Natyrisht, gjithashtu do të ishte e nevojshme të bëja 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, pasi që kisha disa përvojë me të, si dhe shërbimet tona në Python bashkëpunonin me të. Dhe e dini çfarë zbuloj? Ajo është në POCO! Por conan nuk e di që ajo është në POCO dhe nuk di ta ndërtojë atë, në depo ndodhet një skedë konfiguruese e vjetruar (kisha shkruar tashmë për këtë gabim tek krijuesit e POCO). Pra, do të duhet të kërkoj një bibliotekë tjetër.

Dhe kështu, zgjedhja ime ra te një bibliotekë më pak të njohur libpg. Dhe kam pasur një fat të madh, ajo ishte tashmë në conan dhe madje ishte ndërtuar dhe kompozuar.

Hapi tjetër ishte të shkruaja një shembull shërbimi që mund të trajtojë kërkesat.
Ne duhet ta trashëgojmë klasën tonë TemplateServerApp nga Poco::Util::ServerApplication dhe të rivendosim 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();  // wait for CTRL-C or kill

    cerr << "Shutting down..." << endl;
    s.stop();

    return Application::EXIT_OK;
}

Në metodën main duhet të përcaktojmë parametrat: portin, sasinë eThreads dhe madhësinë e radhës. Dhe e gjithë e rëndësishmja, duhet të përcaktojmë trajtuesin për kërkesat që vijnë. Kjo bëhet përmes krijimit të një fabrike

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 kam krijuar një shabllon klase për të punuar me PostgreSQL. Për të ekzekutuar një SQL të thjeshtë, si p.sh. krijimi i një tavoline, ekziston metoda ExecuteSQL(). Për kërkesa më të ndërlikuara ose për të marrë të dhëna, do t'ju duhet të merrni lidhjen përmes GetConnection() dhe të përdorni API-në e 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ë gjithë parametrat për lidhjen me bazën e të dhënave merren nga mjedisi, kështu që duhet të krijoni dhe konfiguroni gjithashtu skedarin .env

.env

DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432

Mund ta shikoni të gjithë kodin në GitHub.

Mikroshërbime në C++. Trillim apo realitet?

Dhe erdhi radha e fundit për të shkruar dockerfile dhe docker-compose.yml. Do ta them sinqerisht, kjo mori pjesën më të madhe të kohës, dhe jo vetëm për shkak se isha fillestar, që ishte e nevojshme të rindërtohej çdo herë libraritë, por për shkak të pengesave me conan. Për shembull, që conan të shkarkojë, instaloje dhe ndërtojnë varësitë e nevojshme, nuk mjafton të shkruhet «conan install .», duhet të transmetoni gjithashtu parametrin -s compiler.libcxx=libstdc++11, nd otherwise you risk getting a lot of errors at the linking stage of your application. Kam kaluar disa orë me këtë gabim, dhe shpresoj se ky artikull do t'i ndihmojë të tjerët ta zgjidhin këtë problem në një kohë më të shkurtër.

Më pas, pas shkruarjes së docker-compose.yml, me rekomandimin e mikut tim, kam shtuar mbështetje cookiecutter dhe tani mund të merrni një shabllon të plotë për një shërbim REST API në C++, me një mjedis të konfiguruar, dhe një PostgreSQL të ngritur, thjesht duke shkruar në konsolën «cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git». Pastaj «docker-compose up —build».

Shpresoj që ky shabllon të ndihmojë të rinjtë në rrugën e tyre të vështirë të zhvillimit të aplikacioneve REST API në gjuhën e madhe dhe të fuqishme, por kaq të ngadalshme si C++.
Gjithashtu, ju rekomandoj shumë të lexoni këtë artikull. Në të, shpjegohet më në detaje se si të punoni me POCO dhe të shkruani shërbimin tuaj REST API.

Burimi: habr.com

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster