Mikroservisi u C++. Fikcija ili stvarnost?

Mikroservisi u C++. Fikcija ili stvarnost?

U ovom ću članku govoriti o tome kako sam stvorio predložak (cookiecutter) i postavio okruženje za pisanje REST API usluge u C++ koristeći docker/docker-compose i upravitelj paketa conan.

Tijekom sljedećeg hackathona, na kojem sam sudjelovao kao backend developer, postavilo se pitanje čime napisati sljedeći mikroservis. Sve što je do sada napisano napisala sam ja i moji drug u Pythonu, budući da je moj kolega bio stručnjak u ovom području i profesionalno je razvijao backendove, dok sam ja općenito bio programer ugrađenih sustava i pisao sam na sjajnom i užasnom C++, a Python sam naučio tek na fakultetu.

Dakle, suočili smo se sa zadatkom pisanja visokoopterećene usluge, čija je glavna zadaća bila pretprocesirati podatke koji dolaze i upisati ih u bazu podataka. I nakon još jedne pauze, prijatelj mi je predložio da, kao C++ programer, napišem ovu uslugu koristeći profesionalce. Tvrdi se da će biti brži, produktivniji i općenito, žiri će biti oduševljen time kako znamo kako upravljati resursima tima. Na što sam odgovorio da nikada nisam radio takve stvari u C++-u i lako bih mogao posvetiti preostalih 20+ sati traženju, kompajliranju i povezivanju odgovarajućih biblioteka. Jednostavno rečeno, uplašio sam se. To je ono za što smo se odlučili i mirno završili sve u Pythonu.

Sada, tijekom prisilne samoizolacije, odlučio sam smisliti kako pisati usluge u C++. Prvo što je trebalo učiniti bilo je odlučiti se za odgovarajuću knjižnicu. Moj izbor je pao na POCO, jer je napisan u objektno orijentiranom stilu i također se može pohvaliti normalnom dokumentacijom. Također, postavilo se pitanje odabira sustava montaže. Do ove točke radio sam samo s Visual Studio, IAR i golim make datotekama. I niti jedan od ovih sustava nije mi se dopao, budući da sam planirao pokrenuti cijelu uslugu u docker kontejneru. Onda sam odlučio pokušati shvatiti cmake i zanimljiv upravitelj paketa Conan. Ovaj upravitelj paketa omogućio vam je registraciju svih ovisnosti u jednoj datoteci

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

[generatori] cmake

i jednostavnom naredbom "conan install ." instalirajte potrebne biblioteke. Naravno, također je bilo potrebno napraviti promjene u

CMakeLists.txt

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

Nakon toga sam počeo tražiti biblioteku za rad s PostgreSQL-om, budući da je to bila ona s kojom sam imao malo iskustva u radu, a to je bila i ona s kojom su naši Python servisi komunicirali. I znate li što sam naučio? U POCO je! Ali conan ne zna da je u POCO-u i ne zna kako ga izgraditi; u repozitoriju postoji zastarjela konfiguracijska datoteka (o ovoj pogrešci sam već pisao kreatorima POCO-a). To znači da ćete morati potražiti drugu knjižnicu.

A onda je moj izbor pao na manje popularnu knjižnicu libpg. I imao sam nevjerojatnu sreću, već je bio u conanu i čak se sastavljao i sastavljao.

Sljedeći korak bio je napisati predložak usluge koji može obraditi zahtjeve.
Moramo naslijediti našu klasu TemplateServerApp od Poco::Util::ServerApplication i nadjačati glavnu metodu.

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

U glavnoj metodi moramo postaviti parametre: port, broj niti i veličinu reda čekanja. I što je najvažnije, morate navesti rukovatelja za dolazne zahtjeve. To se postiže stvaranjem tvornice

TemplateRequestHandlerFactory

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

U mom slučaju, jednostavno svaki put stvara isti rukovatelj - TemplateServerAppHandler. Ovdje možemo smjestiti svoju poslovnu logiku.

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

Također sam izradio predložak klase za rad s PostgreSQL-om. Za izvođenje jednostavnog SQL-a, kao što je stvaranje tablice, postoji metoda IzvršiSQL(). Za složenije upite ili dohvaćanje podataka, morat ćete uspostaviti vezu putem GetConnection() i koristite libpg API. (Možda kasnije ispravim ovu nepravdu).

Baza podataka

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

Svi parametri za povezivanje s bazom preuzeti su iz okruženja, pa je potrebno izraditi i konfigurirati i .env datoteku

.NS

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

Sve kodove možete vidjeti na github.

Mikroservisi u C++. Fikcija ili stvarnost?

A sada dolazi posljednja faza pisanja dockerfilea i docker-compose.yml. Da budem iskren, ovo je oduzelo većinu vremena, i to ne samo zato što sam noob, jer je bilo potrebno svaki put iznova graditi biblioteke, već zbog zamki conana. Na primjer, da bi conan mogao preuzeti, instalirati i izgraditi potrebne ovisnosti, nije dovoljno da preuzme “conan install .”, također treba proslijediti parametar -s compiler.libcxx=libstdc++11, inače riskirate da dobijete hrpu pogrešaka u fazi povezivanja svoje aplikacije. Zaglavio sam s ovom pogreškom nekoliko sati i nadam se da će ovaj članak pomoći drugima da riješe ovaj problem u kraćem roku.

Zatim, nakon što sam napisao docker-compose.yml, na savjet svog prijatelja, dodao sam podršku rezač za kolače i sada si možete nabaviti potpuni predložak za REST API uslugu u C++-u, s prilagođenim okruženjem i instaliranim PostgreSQL-om, jednostavnim unosom "cookiecutter" u konzolu https://github.com/KovalevVasiliy/cpp_rest_api_template.git" A zatim “docker-compose up —build”.

Nadam se da će ovaj predložak pomoći početnicima na njihovom teškom putu razvoja REST API aplikacija u sjajnom i moćnom, ali tako nespretnom jeziku kao što je C++.
Također, toplo preporučujem čitanje ovdje ovo članak. Detaljnije objašnjava kako raditi s POCO i napisati vlastitu REST API uslugu.

Izvor: www.habr.com

Dodajte komentar