Mikropaslaugos C++. Pramonė ar realybė?

Mikropaslaugos C++. Pramonė ar realybė?

Šiame straipsnyje kalbėsiu apie tai, kaip sukūriau šabloną (cookiecutter) ir sukūriau aplinką REST API paslaugai rašyti C++ naudojant docker/docker-compose ir conan paketų tvarkyklę.

Per kitą hakatoną, kuriame dalyvavau kaip backend kūrėjas, iškilo klausimas, ką naudoti norint parašyti kitą mikroservisą. Viskas, kas buvo parašyta iki šiol, parašyta aš ir mano bendražygis Python, nes mano kolega buvo šios srities ekspertas ir profesionaliai kūrė backends, o aš paprastai buvau įterptinių sistemų kūrėjas ir rašiau puikia ir siaubinga C++, o Python ką tik išmokau universitete.

Taigi, susidūrėme su užduotimi parašyti didelės apkrovos paslaugą, kurios pagrindinė užduotis buvo iš anksto apdoroti į ją ateinančius duomenis ir įrašyti į duomenų bazę. Ir po dar vienos dūmų pertraukos draugas pasiūlė man, kaip C++ kūrėjui, parašyti šią paslaugą pasinaudojant profesionalais. Teigiama, kad tai bus greitesnė, produktyvesnė ir apskritai žiuri džiaugsis, kaip mes žinome, kaip valdyti komandos išteklius. Į ką aš atsakiau, kad niekada nedariau tokių dalykų C++ ir galiu nesunkiai skirti likusias 20+ valandų tinkamų bibliotekų paieškai, kompiliavimui ir susiejimui. Paprasčiau tariant, aš nusiminiau. Taip nusprendėme ir viską ramiai užbaigėme Python.

Dabar, priverstinės saviizoliacijos metu, nusprendžiau sugalvoti, kaip rašyti paslaugas C++ kalba. Pirmas dalykas, kurį reikėjo padaryti, buvo nuspręsti dėl tinkamos bibliotekos. Mano pasirinkimas krito POCO, nes jis buvo parašytas į objektą orientuotu stiliumi ir taip pat gali pasigirti įprasta dokumentacija. Taip pat iškilo klausimas dėl surinkimo sistemos pasirinkimo. Iki šiol dirbau tik su Visual Studio, IAR ir plikomis makefilais. Ir nė viena iš šių sistemų manęs nepatraukė, nes planavau visą paslaugą paleisti dokerio konteineryje. Tada nusprendžiau pabandyti išsiaiškinti cmake ir įdomų paketų tvarkyklę conan. Ši paketų tvarkyklė leido užregistruoti visas priklausomybes viename faile

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

[generatoriai] cmake

ir su paprasta komanda "conan install". įdiegti reikiamas bibliotekas. Natūralu, kad taip pat reikėjo keisti

CMakeLists.txt

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

Po to pradėjau ieškoti bibliotekos, kad galėčiau dirbti su PostgreSQL, nes su ja turėjau mažai patirties ir su ja bendravo mūsų Python paslaugos. Ir ar žinai, ko aš išmokau? Tai POCO! Bet conan nežino, kad jis yra POCO, ir nežino, kaip jį sukurti; saugykloje yra pasenęs konfigūracijos failas (apie šią klaidą jau rašiau POCO kūrėjams). Tai reiškia, kad turėsite ieškoti kitos bibliotekos.

Ir tada mano pasirinkimas krito į mažiau populiarią biblioteką libpg. Ir man nepaprastai pasisekė, jis jau buvo baigtas ir netgi buvo surenkamas ir montuojamas.

Kitas žingsnis buvo parašyti paslaugos šabloną, galintį apdoroti užklausas.
Turime paveldėti savo TemplateServerApp klasę iš Poco::Util::ServerApplication ir nepaisyti pagrindinio metodo.

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

Pagrindiniame metode turime nustatyti parametrus: prievadą, gijų skaičių ir eilės dydį. Ir, svarbiausia, turite nurodyti gaunamų užklausų tvarkytoją. Tai daroma kuriant gamyklą

TemplateRequestHandlerFactory

class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
    virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
    {
        return new TemplateServerAppHandler;
    }
};

Mano atveju jis tiesiog kiekvieną kartą sukuria tą patį tvarkyklę – TemplateServerAppHandler. Čia galime išdėstyti savo verslo logiką.

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

Taip pat sukūriau klasės šabloną darbui su PostgreSQL. Norint atlikti paprastą SQL, pavyzdžiui, sukurti lentelę, yra metodas VykdytiSQL(). Norėdami atlikti sudėtingesnes užklausas ar gauti duomenis, turėsite užmegzti ryšį per Gauti ryšį () ir naudokite libpg API. (Gal vėliau šią neteisybę ištaisysiu).

duomenų bazė

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

Visi prisijungimo prie duomenų bazės parametrai yra paimti iš aplinkos, todėl taip pat reikia sukurti ir sukonfigūruoti .env failą

.NS

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

Visą kodą galite pamatyti adresu github.

Mikropaslaugos C++. Pramonė ar realybė?

Ir dabar ateina paskutinis dockerfile ir docker-compose.yml rašymo etapas. Tiesą sakant, tai užtrukdavo didžiąją laiko dalį, ir ne tik todėl, kad esu niekšas, nes kiekvieną kartą reikėjo atkurti bibliotekas, bet ir dėl „conan“ spąstų. Pavyzdžiui, norint, kad „conan“ galėtų atsisiųsti, įdiegti ir sukurti reikiamas priklausomybes, neužtenka atsisiųsti „conan install .“, jis taip pat turi perduoti parametrą -s compiler.libcxx=libstdc++11, kitu atveju rizikuojate gauti daugybę klaidų susiedami savo programą. Kelias valandas įstrigo ši klaida ir tikiuosi, kad šis straipsnis padės kitiems žmonėms išspręsti šią problemą per trumpesnį laiką.

Toliau, parašęs docker-compose.yml, draugo patarimu, pridėjau palaikymą Sausainių formelės ir dabar galite gauti visavertį REST API paslaugos šabloną C++, su pritaikyta aplinka ir įdiegta PostgreSQL, tiesiog įvesdami „cookiecutter“ į konsolę https://github.com/KovalevVasiliy/cpp_rest_api_template.git“ Ir tada „sukomponuoti – pastatyti“.

Tikiuosi, kad šis šablonas padės pradedantiesiems sudėtingame kelyje kuriant REST API programas puikia ir galinga, bet tokia gremėzdiška kalba kaip C++.
Be to, labai rekomenduoju paskaityti čia tai straipsnis. Jame išsamiau paaiškinama, kaip dirbti su POCO ir sukurti savo REST API paslaugą.

Šaltinis: www.habr.com

Добавить комментарий