Mikroslužby v C++. Fikcia alebo realita?

Mikroslužby v C++. Fikcia alebo realita?

V tomto článku budem hovoriť o tom, ako som vytvoril šablónu (cookiecutter) a nastavil prostredie na písanie služby REST API v C++ pomocou docker/docker-compose a správcu balíkov conan.

Pri ďalšom hackathone, ktorého som sa zúčastnil ako backend developer, vyvstala otázka, čo použiť na napísanie ďalšej mikroslužby. Všetko, čo bolo doteraz napísané, som napísal ja a môj súdruh v Pythone, keďže môj kolega bol odborníkom v tejto oblasti a profesionálne vyvíjal backendy, zatiaľ čo ja som bol vo všeobecnosti vývojár vstavaných systémov a písal som v skvelom a hroznom C++ a Python som sa práve naučil na univerzite.

Stáli sme teda pred úlohou napísať vysoko zaťaženú službu, ktorej hlavnou úlohou bolo predspracovať do nej prichádzajúce dáta a zapísať ich do databázy. A po ďalšej dymovej prestávke mi priateľ navrhol, aby som ako vývojár C++ napísal túto službu pomocou profesionálov. Argumentujúc tým, že to bude rýchlejšie, produktívnejšie a vo všeobecnosti bude porota potešená tým, ako vieme spravovať zdroje tímu. Na to som odpovedal, že takéto veci som v C++ nikdy nerobil a zvyšných 20+ hodín by som pokojne mohol venovať vyhľadávaniu, kompilovaniu a prepájaniu vhodných knižníc. Jednoducho povedané, vykašlal som sa. To je to, pre čo sme sa rozhodli a pokojne dokončili všetko v Pythone.

Teraz, počas nútenej samoizolácie, som sa rozhodol prísť na to, ako písať služby v C++. Ako prvé bolo potrebné rozhodnúť sa pre vhodnú knižnicu. Moja voľba padla POCO, keďže bol napísaný objektovo orientovaným štýlom a pýšil sa aj normálnou dokumentáciou. Taktiež vyvstala otázka výberu montážneho systému. Do tejto chvíle som pracoval iba s Visual Studio, IAR a holými makefiles. A ani jeden z týchto systémov ma neoslovil, keďže celú službu som plánoval spustiť v docker kontajneri. Potom som sa rozhodol, že skúsim prísť na cmake a zaujímavého správcu balíkov Conan. Tento správca balíkov vám umožnil zaregistrovať všetky závislosti v jednom súbore

conanfile.txt
[vyžaduje] poco/1.9.3
libpq/11.5

[generátory] cmake

a jednoduchým príkazom "conan install." nainštalovať potrebné knižnice. Prirodzene, bolo potrebné vykonať aj zmeny

CMakeLists.txt

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

Potom som začal hľadať knižnicu na prácu s PostgreSQL, pretože to bola knižnica, s ktorou som nemal veľa skúseností, a tiež to bola tá, s ktorou interagovali naše služby Python. A viete, čo som sa naučil? Je to v POCO! Conan však nevie, že je v POCO a nevie, ako ho zostaviť, v úložisku je zastaraný konfiguračný súbor (o tejto chybe som už písal tvorcom POCO). To znamená, že budete musieť hľadať inú knižnicu.

A potom moja voľba padla na menej obľúbenú knižnicu libpg. A mal som neskutočné šťastie, už bol v conane a dokonca sa montoval a montoval.

Ďalším krokom bolo napísanie šablóny služby, ktorá dokáže spracovať požiadavky.
Musíme zdediť našu triedu TemplateServerApp od Poco::Util::ServerApplication a prepísať hlavnú metódu.

Aplikácia 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;
}

V hlavnej metóde musíme nastaviť parametre: port, počet vlákien a veľkosť frontu. A čo je najdôležitejšie, musíte zadať obsluhu pre prichádzajúce požiadavky. To sa deje vytvorením továrne

TemplateRequestHandlerFactory

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

V mojom prípade jednoducho vždy vytvorí rovnaký obslužný program - TemplateServerAppHandler. Tu môžeme umiestniť našu obchodnú 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();
    }
};

Vytvoril som tiež šablónu triedy na prácu s PostgreSQL. Na vykonanie jednoduchého SQL, ako je vytvorenie tabuľky, existuje metóda ExecuteSQL(). Pre zložitejšie otázky alebo získavanie údajov budete musieť získať spojenie cez GetConnection() a použite libpg API. (Snáď neskôr túto nespravodlivosť napravím).

databázy

#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 pre pripojenie k databáze sú prevzaté z prostredia, takže je potrebné vytvoriť a nakonfigurovať aj súbor .env

.env

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

Celý kód môžete vidieť na github.

Mikroslužby v C++. Fikcia alebo realita?

A teraz prichádza posledná fáza písania súboru dockerfile a docker-compose.yml. Úprimne povedané, zabralo to väčšinu času, a to nielen preto, že som noob, pretože bolo potrebné zakaždým prestavať knižnice, ale aj kvôli nástrahám conanu. Napríklad, aby si conan stiahol, nainštaloval a vytvoril potrebné závislosti, nestačí mu stiahnuť „conan install .“, ale musí odovzdať aj parameter -s kompilátor.libcxx=libstdc++11, inak riskujete, že vo fáze prepojenia vašej aplikácie dostanete veľa chýb. S touto chybou som sa zasekol niekoľko hodín a dúfam, že tento článok pomôže ostatným vyriešiť tento problém za kratší čas.

Ďalej, po napísaní docker-compose.yml som na radu môjho priateľa pridal podporu vykrajovač na sušienky a teraz si môžete získať plnohodnotnú šablónu pre službu REST API v C++, s prispôsobeným prostredím a nainštalovaným PostgreSQL, jednoducho zadaním „cookiecutter“ do konzoly 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 aplikácií REST API vo skvelom a výkonnom, ale takom nemotornom jazyku, akým je C++.
Tiež veľmi odporúčam prečítať si tu toto článok. Podrobnejšie vysvetľuje, ako pracovať s POCO a napísať si vlastnú službu REST API.

Zdroj: hab.com

Pridať komentár