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

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

Në këtë artikull do të flas se si kam krijuar një shabllon (cookiecutter) dhe kam vendosur një mjedis për të shkruar një shërbim REST API në C++ duke përdorur docker/docker-compose dhe menaxherin e paketave conan.

Gjatë hackathon-it të ardhshëm, në të cilin mora pjesë si zhvillues i backend-it, lindi pyetja se çfarë të përdorja për të shkruar mikroshërbimin e ardhshëm. Gjithçka që është shkruar deri tani është shkruar nga unë dhe imja shok në Python, meqenëse kolegu im ishte ekspert në këtë fushë dhe zhvilloi profesionalisht backends, ndërsa unë në përgjithësi isha një zhvillues i sistemeve të ngulitura dhe shkruaja në C++ të mrekullueshëm dhe të tmerrshëm, dhe sapo mësova Python në universitet.

Pra, ne u përballëm me detyrën e shkrimit të një shërbimi me ngarkesë të lartë, detyra kryesore e të cilit ishte të përpunonte paraprakisht të dhënat që vinin tek ai dhe t'i shkruante ato në bazën e të dhënave. Dhe pas një pushimi tjetër tymi, një mik sugjeroi që unë, si zhvillues i C++, ta shkruaj këtë shërbim duke përdorur profesionistët. Argumentimi për këtë është se do të jetë më i shpejtë, më produktiv dhe në përgjithësi, juria do të jetë e kënaqur me mënyrën se si ne dimë të menaxhojmë burimet e ekipit. Për të cilën unë u përgjigja se nuk kisha bërë kurrë gjëra të tilla në C++ dhe mund t'i kushtoja lehtësisht 20+ orët e mbetura kërkimit, përpilimit dhe lidhjes së bibliotekave të përshtatshme. Ta themi thjesht, u largova. Kjo është ajo që vendosëm dhe përfunduam me qetësi gjithçka në Python.

Tani, gjatë izolimit të detyruar, vendosa të kuptoj se si të shkruaj shërbimet në C++. Gjëja e parë që duhet bërë ishte të vendosni për një bibliotekë të përshtatshme. Zgjedhja ime ra POCO, pasi ishte shkruar në një stil të orientuar nga objekti dhe gjithashtu mburrej me dokumentacion normal. Gjithashtu, u ngrit pyetja në lidhje me zgjedhjen e një sistemi montimi. Deri në këtë pikë kam punuar vetëm me Visual Studio, IAR dhe skedarë të zhveshur. Dhe asnjë nga këto sisteme nuk më tërheqë mua, pasi kisha planifikuar të drejtoja të gjithë shërbimin në një kontejner docker. Pastaj vendosa të përpiqem të kuptoj cmake dhe një menaxher interesant paketash conan. Ky menaxher i paketave ju lejoi të regjistroni të gjitha varësitë në një skedar

conanfile.txt
[kërkon] poco/1.9.3
libpq/11.5

[gjeneruesit] cmake

dhe me një komandë të thjeshtë "conan install ." instaloni bibliotekat e nevojshme. Natyrisht, ishte gjithashtu e nevojshme të bëhen ndryshime në

CMakeLists.txt

include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})

Pas kësaj, fillova të kërkoja një bibliotekë për të punuar me PostgreSQL, pasi ishte biblioteka me të cilën kisha pak përvojë pune, dhe ishte gjithashtu ajo me të cilën ndërvepruan shërbimet tona Python. Dhe a e dini se çfarë mësova? Është në POCO! Por Conan nuk e di që është në POCO dhe nuk di se si ta ndërtojë; ekziston një skedar konfigurimi i vjetëruar në depo (u kam shkruar tashmë për këtë gabim krijuesve të POCO). Kjo do të thotë që ju do të duhet të kërkoni për një bibliotekë tjetër.

Dhe pastaj zgjedhja ime ra në një bibliotekë më pak të njohur libpg. Dhe unë isha jashtëzakonisht me fat, ishte tashmë në conan dhe madje po montohej dhe montohej.

Hapi tjetër ishte të shkruani një model shërbimi që mund të përpunojë kërkesat.
Ne duhet të trashëgojmë klasën tonë TemplateServerApp nga Poco::Util::ServerApplication dhe të anashkalojmë metodën kryesore.

TemplateServer App

#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;
}

Në metodën kryesore duhet të vendosim parametrat: portin, numrin e thread-eve dhe madhësinë e radhës. Dhe më e rëndësishmja, duhet të specifikoni një mbajtës për kërkesat 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, ai thjesht krijon të njëjtin mbajtës çdo herë - TemplateServerAppHandler. Këtu mund të vendosim logjikën tonë të biznesit.

TemplateServerApp Handler

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();
    }
};

Krijova gjithashtu një shabllon klase për të punuar me PostgreSQL. Për të kryer SQL të thjeshtë, siç është krijimi i një tabele, ekziston një metodë ExecuteSQL(). Për pyetje më komplekse ose rikthim të të dhënave, do t'ju duhet të merrni një lidhje nëpërmjet GetConnection () dhe 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ë gjithë parametrat për lidhjen me bazën e të dhënave janë marrë nga mjedisi, kështu që ju gjithashtu duhet të krijoni dhe konfiguroni skedarin .env

.zili

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

Ju mund të shihni të gjithë kodin në github.

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

Dhe tani vjen faza e fundit e shkrimit të dockerfile dhe docker-compose.yml. Për të qenë i sinqertë, kjo mori pjesën më të madhe të kohës, dhe jo vetëm sepse unë jam noob, sepse ishte e nevojshme të rindërtonim bibliotekat çdo herë, por për shkak të grackave të Conan. Për shembull, në mënyrë që conan të shkarkojë, instalojë dhe të ndërtojë varësitë e nevojshme, nuk mjafton që ai të shkarkojë "conan install .", ai gjithashtu duhet të kalojë parametrin -s compiler.libcxx=libstdc++11, përndryshe rrezikoni të merrni një sërë gabimesh në fazën e lidhjes së aplikacionit tuaj. Unë kam ngecur me këtë gabim për disa orë dhe shpresoj se ky artikull do t'i ndihmojë njerëzit e tjerë ta zgjidhin këtë problem në më pak kohë.

Më pas, pasi shkruajta docker-compose.yml, me këshillën e mikut tim, shtova mbështetje prerese biskotash dhe tani mund të merrni vetes një shabllon të plotë për një shërbim REST API në C++, me një mjedis të personalizuar dhe PostgreSQL të instaluar, thjesht duke futur "cookiecutter" në tastierë https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Dhe pastaj "docker-compose up -build".

Shpresoj se ky shabllon 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ë fuqishme, por një gjuhë kaq të ngathët si C++.
Gjithashtu, unë rekomandoj shumë të lexoni këtu kjo artikull. Ai shpjegon më në detaje se si të punoni me POCO dhe të shkruani shërbimin tuaj REST API.

Burimi: www.habr.com

Shto një koment