Mikroslužby v C++. Vymyslené alebo realita?

Mikroslužby v C++. Vymyslené alebo realita?

V tomto článku sa podelím o to, ako som vytvoril šablónu (cookiecutter) a nastavil prostredie pre písanie REST API služby v C++ s použitím docker/docker-compose a správcu balíkov conan.

Počas ďalšieho hackathonu, kde som sa zúčastnil ako backend vývojár, sa objavila otázka, na čom napísať ďalšiu mikro službu. Všetko, čo bolo zatiaľ napísané, som vytvoril ja a môj spolubojovník v jazyku Python, pretože môj kolega bol odborníkom v tejto oblasti a profesionálne sa venoval vývoju backendov, zatiaľ čo ja som bol v podstate vývojár pre zabudované systémy a písal som v skvelom a hroznom C++, zatiaľ čo Python som sa len naučil na univerzite.

Takže sme mali úlohu napísať vysoko zaťaženú službu, ktorej hlavnou úlohou bol predspracovanie prichádzajúcich dát a ich zápis do databázy. A po ďalšej prestávke mi kolega navrhol, aby som, ako C++ vývojár, napísal túto službu v C++. Argumentoval tým, že to bude rýchlejšie, výkonnejšie a vôbec, porota bude nadšená z toho, ako vieme využívať zdroje tímu. Na čo som odpovedal, že som nikdy takéto veci na C++ nerobil a s ľahkosťou môžem posledných 20+ hodín venovať hľadaniu, kompilácii a skladaniu vhodných knižníc. Jednoducho povedané, zľakol som sa. Na tom sme sa dohodli a pokojne sme všetko dokončili v Pythone.

Teraz, počas nútenej izolácie, som sa rozhodol pochopiť, ako písať služby v C++. Prvá vec, ktorú som potreboval urobiť, bola určiť vhodnú knižnicu. Môj výber padol na POCO, pretože bola napísaná v objektovo orientovanom štýle a mohla sa pochváliť dobrou dokumentáciou. Takisto sa objavila otázka voľby systému zostavenia. Predtým som pracoval len s Visual Studio, IAR a "holými" makefile. A žiadny z týchto systémov ma nenadchol, pretože som plánoval spustiť celú službu v docker kontejnere. Potom som sa rozhodol pokúsiť sa pochopiť cmake a zaujímavého správcu balíkov conan. Tento správca balíkov umožňoval zapísať všetky závislosti do jedného súboru

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

[generators]
cmake

a s pomocou jednoduchého príkazu „conan install .“ nainštalovať potrebné knižnice. Samozrejme, taktiež bolo potrebné vykonať zmeny v

CMakeLists.txt

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

Potom som začal hľadať knižnicu na prácu s PostgreSQL, pretože s ňou som mal trochu skúseností a naše služby v Pythone s ňou interagovali. A viete, čo som zistil? Nachádza sa v POCO! Ale conan nevie, že je v POCO a nevie ju zostavovať, v repozitári je zastaraný konfiguračný súbor (už som o tejto chybe napísal tvorcom POCO). Takže budem musieť hľadať inú knižnicu.

A tak som si vybral menej populárnu knižnicu. libpg. A mal som obrovské šťastie, už bola v conan a dokonca sa zostavovala a spojovala.

Ďalším krokom bolo napísať šablónu služby, ktorá dokáže spracovávať požiadavky.
Musíme zdediť našu triedu TemplateServerApp z Poco::Util::ServerApplication a prepísať metódu 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;
}

V metóde main musíme nastaviť parametre: port, počet vlákien a veľkosť fronty. A hlavne, musíme nastaviť spracovateľ príchozích požiadaviek. Robí sa to vytvorením fabriky.

TemplateRequestHandlerFactory

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

V mojom prípade jednoducho zakaždým vytvára ten istý spracovateľ — TemplateServerAppHandler. Práve tu môžeme umiestniť našu biznis 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();
    }
};

Tak som vytvoril šablónu triedy na prácu s PostgreSQL. Na jednoduché SQL, napríklad na vytvorenie tabuľky, je k dispozícii metóda ExecuteSQL(). Pre zložitejšie dotazy alebo získavanie údajov je potrebné získať pripojenie cez GetConnection() a použiť API libpg. (Možno to neskôr opravím).

Databáza

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

Všetky parametre na pripojenie k databáze berieme z prostredia, takže musíte tiež vytvoriť a nastaviť súbor .env

.env

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

Môžete si pozrieť celý kód na gite.

Mikroslužby v C++. Vymyslené alebo realita?

A prišiel posledný krok písania dockerfile a docker-compose.yml. Priznám sa, že na to ušla veľká časť času, a nie len preto, že som začiatočník, ktorý musel zakaždým prekompilovať knižnice, ale aj kvôli skrytým problémom v conan. Napríklad, aby conan stiahol, nainštaloval a postavil potrebné závislosti, nestačí stiahnuť „conan install .“, musí sa tiež zadať parameter -s compiler.libcxx=libstdc++11, inak riskujete, že dostanete kopu chýb pri linkovaní aplikácie. S touto chybou som sedel niekoľko hodín a dúfam, že tento článok pomôže iným ľuďom vyriešiť tento problém rýchlejšie.

Ďalej, po napísaní docker-compose.yml, na radu svojho kamaráta som pridal podporu cookiecutter a teraz si môžete získať plnohodnotnú šablónu pre REST API službu v C++, s nastaveným prostredím a so spustenou PostgreSQL, stačí zadať do konzoly „cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git“. A potom „docker-compose up —build“.

Dúfam, že táto šablóna pomôže začiatočníkom na ich náročnej ceste vývoja REST API aplikácií vo veľkom a mocnom, ale tak nešikovnom jazyku, ako je C++.
Takisto veľmi odporúčam prečítať si túto tento článok. V ňom je podrobnejšie vysvetlené, ako pracovať s POCO a ako napísať svoj REST API servis.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster