Mikroslužby v C++. Fantasie nebo realita?

Mikroslužby v C++. Fantasie nebo realita?

V tomto článku vám ukážu, jak jsem vytvořil šablonu (cookiecutter) a nastavil prostředí pro psaní REST API služby v C++ s využitím docker/docker-compose a balíčkovacího správce conan.

Během dalšího hackathonu, kterého jsem se účastnil jako backendový vývojář, se objevila otázka, na čem napsat další mikroslužbu. Vše, co bylo dosud napsáno, jsem napsal já a můj kamarád v jazyce Python, protože můj kolega byl specialistou v této oblasti a profesionálně se zabýval vývojem backendů, zatímco já jsem byl vlastně vývojářem pro vestavěné systémy a psal jsem v úžasném a děsivém C++, zatímco Python jsem se našprtal na univerzitě.

Takže před námi stála úloha napsat vysoce zatěžovaný servis, jehož hlavním úkolem bylo předzpracování přicházejících dat a jejich zápis do DB. A po další přestávce mi kamarád navrhl, abych jako C++ vývojář napsal tento servis v plusách. Argumentoval tím, že to bude rychlejší, výkonnější, a že porota bude nadšená tím, jak dokážeme hospodařit s prostředky týmu. Na to jsem odpověděl, že jsem se nikdy takovými věcmi v C++ nezabýval a mohu s lehkostí věnovat zbývající čas 20+ hodin hledání, kompilaci a skládání vhodných knihoven. Zjednodušeně řečeno, bál jsem se. Na tom jsme se dohodli a v klidu dolaďovali všechno v Pythonu.

Nyní, během nucené izolace, jsem se odvážil pochopit, jak psát služby v C++. První, co bylo třeba udělat, bylo vybrat vhodnou knihovnu. Můj výběr padl na POCO, protože byla napsána objektově orientovaným stylem a mohla se pyšnit dobrou dokumentací. Také se objevila otázka výběru systému sestavení. Do tohoto okamžiku jsem pracoval pouze s Visual Studio, IAR a "holými" makefile. A žádný z těchto systémů mě příliš nelákal, protože jsem plánoval spustit celý servis v dockerovém kontejneru. Tak jsem se rozhodl zkusit se seznámit s cmake a zajímavým balíčkovacím správcem conan. Tento balíčkovací správce umožňoval napsat všechny závislosti v jednom souboru

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

[generators]
cmake

a pomocí jednoduchého příkazu „conan install .“ nainstalovat potřebné knihovny. Samozřejmě bylo také nutné provést změny v

CMakeLists.txt

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

Poté jsem začal hledat knihovnu pro práci s PostgreSQL, protože jsem s ní měl určité zkušenosti a také naše služby na Pythonu s ní interagovaly. A víte, co jsem zjistil? Je v POCO! Ale conan neví, že je v POCO a neumí ji sestavit, v repozitáři leží zastaralý konfigurační soubor (o této chybě jsem již napsal tvůrcům POCO). To znamená, že budu muset hledat jinou knihovnu.

A tak jsem si vybral méně populární knihovnu libpg. A měl jsem obrovské štěstí, byla již v conan a dokonce se sestavovala a linkovala.

Dalším krokem bylo napsat šablonu služby, která umí zpracovávat požadavky.
Musíme zdědit naši třídu TemplateServerApp z Poco::Util::ServerApplication a přepsat metodu 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 metodě main musíme nastavit parametry: port, počet vláken a velikost fronty. A co je nejdůležitější, musíme nastavit zpracovatele příchozích požadavků. To se dělá pomocí vytvoření továrny

TemplateRequestHandlerFactory

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

V mém případě vždy vytváří jeden a ten samý zpracovatel — TemplateServerAppHandler. Právě zde můžeme umístit naši 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();
    }
};

Také jsem vytvořil šablonu třídy pro práci s PostgreSQL. Abych provedl jednoduchý SQL dotaz, například vytvoření tabulky, existuje metoda ExecuteSQL(). Pro složitější dotazy nebo získávání dat bude nutné získat připojení přes GetConnection() a použít API libpg. (Možná později tuto nespravedlnost opravím).

Databáze

#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šechny parametry pro připojení k databázi se berou z prostředí, takže si také musíte vytvořit a nakonfigurovat soubor .env

.env

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

Můžete si prohlédnout celý kód na GitHubu.

Mikroslužby v C++. Fantasie nebo realita?

A přišel poslední krok psaní dockerfile a docker-compose.yml. Řeknu upřímně, to zabralo většinu času, a nejen proto, že jsem nováček, a bylo potřeba pokaždé znovu sestavit knihovny, ale kvůli problémům s conanem. Například, aby conan stáhl, nainstaloval a zkompiloval potřebné závislosti, nestačí mu spustit "conan install .", musí také předat parametr -s compiler.libcxx=libstdc++11, jinak riskujete, že dostanete spoustu chyb při linkování vaší aplikace. S touto chybou jsem strávil několik hodin a doufám, že tento článek pomůže ostatním vyřešit tento problém rychleji.

Poté, po napsání docker-compose.yml, na doporučení svého kamaráda jsem přidal podporu cookiecutter a nyní si můžete vytvořit plnohodnotnou šablonu pro REST API službu na C++, s nastaveným prostředím a spuštěným PostgreSQL, jednoduše zadáním do konzole „cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git“. A poté „docker-compose up --build“.

Doufám, že tato šablona pomůže začátečníkům na jejich obtížné cestě vývoje REST API aplikací ve skvělém a mocném, ale tak neobratném jazyce, jako je C++.
Dále velmi doporučuji přečíst si tenhle tento článek. Detailněji vysvětluje, jak pracovat s POCO a napsat svou vlastní REST API službu.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster