Mikroteenused C++-s. Leiutis või reaalsus?

Mikroteenused C++-s. Leiutis või reaalsus?

В этой статье я расскажу о том, как создал шаблон (cookiecutter) и настроил окружение для написания REST API сервиса на С++ с использованием docker/docker-compose и пакетного менеджера conan.

Во время очередного хакатона, в котором я участвовал в качестве бекенд-разработчика, встал вопрос о том, на чем писать очередной микросервис. Все что было написано на текущий момент, писалось мной и моим товарищем на языке Python, так как мой коллега был специалистом в этой области и профессионально занимался разработкой бекендов, в то время как я вообще являлся разработчиком под встроенные системы и писал на великом и ужасном С++, а Python просто подучил в университете.

Nii et meie ülesanne oli kirjutada kõrge koormusega teenus, mille peamine ülesanne oli sisenevate andmete eeltöötlus ja nende salvestamine andmebaasi. Ja pärast järgmise suitsetamispausi pakkus üks kolleeg mulle, kui C++ arendajale, kirjutada see teenus C++ keeles. Ta põhjendas, et nii saab kiiremini, efektiivsemalt ning žürii on vaimustuses sellest, kuidas me suudame meeskonna ressursse kasutada. Sellele vastasin, et ma ei olnud kunagi selliste asjadega C++-s tegelenud ja suudan kerge vaevaga ülejäänud 20+ tundi pühenduda sobivate raamatukogude otsimisele, kompileerimisele ja sidumisele. Lihtsalt öeldes, ma kartsin. Nii me leppisime kokku ja kirjutasime kõik rahulikult Pythonis.

Praegu, sunnitud isoleerimise ajal, otsustasin ma õppida, kuidas kirjutada teenuseid C++-s. Esimene asi, mida sain teha, oli valida sobiv raamatukogu. Minu valik langes POCO, kuna see oli kirjutatud objekti-orienteritud stiilis ja oli korraliku dokumentatsiooniga. Samuti tekkis küsimus ehitussüsteemi valimisest. Olen enne seda töötanud ainult Visual Studios, IAR-is ja „puhtal” makefile-l. Ükski neist süsteemidest ei tõmmanud mind, kuna plaanisin käivitada kogu teenuse docker-konteineris. Siis otsustasin proovida cmake'i ja huvitavat pakkemanagerit. conan. See pakkemanager võimaldas kõiki sõltuvusi kirjutada ühte faili.

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

[generators]
cmake

ja lihtsa käsu „conan install .” abil installida vajalikud raamatukogud. Loomulikult tuli ka teha muudatused

CMakeLists.txt

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

Seejärel hakkasin otsima PostgreSQL-iga töötamiseks raamatukogu, kuna mul oli selle kasutamise osas väike kogemus ning meie teenused Pythonis suheldes kasutasid just seda. Ja tead, mida ma avastasin? See on POCO-s! Kuid conan ei tea, et see on POCO-s ja ei oska seda ehitada, repovaatetis on aegunud konfiguratsioonifail (ma olen juba sellest veast POCO loojatele kirjutanud). Näib, et tuleb otsima minna teist raamatukogu.

Ja nii langes mu valik vähem populaarsele raamatukogule libpg. Ja mul vedas, see oli juba conan-s ja isegi ehitati ja siduti.

Järgmiseks sammuks oli teenuse šablooni kirjutamine, mis oskab töötleda päringuid.
Peame pärima oma klassi TemplateServerApp klassist Poco::Util::ServerApplication ja üle kirjutama meetodi 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;
}

Peamises meetodis peame määrama parameetrid: port, lõimede arv ja järjekorra suurus. Ja mis kõige tähtsam, peame määrama sisendi päringute töötleja. Seda tehakse tehase loomise kaudu.

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. Just siin saame me paigutada meie äriloogika.

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

Olen samuti loonud klassi šablooni PostgreSQL-iga töötamiseks. Lihtsa SQL-i, näiteks tabeli loomise jaoks, on olemas meetod ExecuteSQL(). Tõsisemate päringute või andmete saamise jaoks tuleb luua ühendus meetodi kaudu GetConnection() ja kasutada libpg API-t. (Võib-olla parandan selle ebaõigluse hiljem).

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 andmebaasi ühendamise parameetrid saadakse keskkonnast, seega peate ka looma ja konfigureerima faili .env

.env

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

Saate vaadata kogu koodi GitHubis.

Mikroteenused C++-s. Leiutis või reaalsus?

Ja on jõudnud viimase etapi kirjutamine dockerfile ja docker-compose.yml. Olen aus, selleks kulus suurem osa ajast, ja mitte ainult sellepärast, et olen algaja, kes pidi iga kord raamatukogusid uuesti kompileerima, vaid ka conan'i takistuste tõttu. Näiteks, et conan saaks vajalikud sõltuvused alla laadida, installida ja üles ehitada, ei piisa vaid "conan install ." käsust, vaid peab lisama parameetri -s compiler.libcxx=libstdc++11, vastasel juhul riskite saada hulga vigu rakenduse linkimise etapil. Veetsin selle vea tõttu mitu tundi ning loodan, et see artikkel aitab teistel inimestel selle probleemi kiiremini lahendada.

Seejärel, pärast docker-compose.yml kirjutamist, oma sõbra soovitusel lisasin toetuse cookiecutter ja nüüd saate endale täieõigusliku шаблoni REST API teenuse jaoks C++ keeles koos seadistatud keskkonna ja PostgreSQL-iga, kirjutades lihtsalt terminalisse "cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git". Ja seejärel "docker-compose up —build".

Loodan, et see шаблон aitab algajaid nende raskel teel REST API rakenduste arendamisel suur ja vägev, kuid nii raske keele nagu C++ puhul.
Samuti soovitan tungivalt lugeda seda artiklis. Selles selgitatakse, kuidas töötada POCO-ga ja kirjutada oma REST API teenus.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster