Mikrostoritve v C++. Fikcija ali resničnost?

Mikrostoritve v C++. Fikcija ali resničnost?

V tem članku bom govoril o tem, kako sem ustvaril predlogo (cookiecutter) in nastavil okolje za pisanje storitve REST API v C++ z uporabo docker/docker-compose in upravitelja paketov conan.

Med naslednjim hackathonom, na katerem sem sodeloval kot backend razvijalec, se je pojavilo vprašanje, s čim napisati naslednjo mikrostoritev. Vse, kar je bilo do sedaj napisano, sem napisal jaz in moji tovariš v Pythonu, saj je bil moj kolega strokovnjak na tem področju in je profesionalno razvijal zaledja, jaz pa sem bil na splošno razvijalec vgrajenih sistemov in sem pisal v odličnem in groznem C++, Pythona pa sem se naučil šele na univerzi.

Tako smo bili postavljeni pred nalogo pisanja visoko obremenjene storitve, katere glavna naloga je bila predobdelava podatkov, ki prihajajo vanjo, in njihovo pisanje v bazo podatkov. In po še enem premoru mi je prijatelj predlagal, naj kot razvijalec C++ napišem to storitev s pomočjo profesionalcev. To trdijo tako, da bo hitrejše, bolj produktivno, na splošno pa bo žirija navdušena nad tem, kako znamo upravljati z viri ekipe. Na kar sem odgovoril, da še nikoli nisem počel takšnih stvari v C++ in bi zlahka posvetil preostalih 20+ ur iskanju, prevajanju in povezovanju ustreznih knjižnic. Preprosto povedano, obupal sem. Tako smo se odločili in vse mirno dokončali v Pythonu.

Zdaj, med prisilno samoizolacijo, sem se odločil ugotoviti, kako pisati storitve v C++. Najprej se je bilo treba odločiti za primerno knjižnico. Moja izbira je padla na POCO, saj je bil napisan v objektno usmerjenem slogu in se je ponašal tudi z običajno dokumentacijo. Pojavilo se je tudi vprašanje izbire montažnega sistema. Do te točke sem delal samo z Visual Studio, IAR in golimi make datotekami. In nobeden od teh sistemov me ni pritegnil, saj sem nameraval zagnati celotno storitev v docker vsebniku. Potem sem se odločil, da poskusim ugotoviti cmake in zanimivega upravitelja paketov Conan. Ta upravitelj paketov vam je omogočil registracijo vseh odvisnosti v eni datoteki

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

[generatorji] cmake

in s preprostim ukazom "conan install ." namestite potrebne knjižnice. Seveda je bilo treba spremeniti tudi v

CMakeLists.txt

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

Po tem sem začel iskati knjižnico za delo s PostgreSQL, saj je bila tista, s katero sem imel malo izkušenj, in je bila tudi tista, s katero so sodelovale naše storitve Python. In veš, kaj sem se naučil? V POCO je! Toda conan ne ve, da je v POCO in ne ve, kako ga zgraditi; v repozitoriju je zastarela konfiguracijska datoteka (o tej napaki sem že pisal ustvarjalcem POCO). To pomeni, da boste morali poiskati drugo knjižnico.

In potem je moja izbira padla na manj priljubljeno knjižnico libpg. In imel sem neverjetno srečo, bil je že v conanu in se je celo sestavljal in sestavljal.

Naslednji korak je bil pisanje storitvene predloge, ki lahko obdeluje zahteve.
Naš razred TemplateServerApp moramo podedovati od Poco::Util::ServerApplication in preglasiti glavno 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;
}

V glavni metodi moramo nastaviti parametre: vrata, število niti in velikost čakalne vrste. In kar je najpomembnejše, določiti morate upravljavca za dohodne zahteve. To se naredi z ustvarjanjem tovarne

TemplateRequestHandlerFactory

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

V mojem primeru preprosto vsakič ustvari isti upravljalnik - TemplateServerAppHandler. Tu lahko umestimo svojo poslovno logiko.

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

Ustvaril sem tudi predlogo razreda za delo s PostgreSQL. Za izvajanje preprostega SQL, kot je ustvarjanje tabele, obstaja metoda ExecuteSQL(). Za zahtevnejše poizvedbe ali pridobivanje podatkov boste morali pridobiti povezavo prek GetConnection() in uporabite API libpg. (Morda kasneje popravim to krivico).

Baze podatkov

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

Vsi parametri za povezavo z bazo podatkov so vzeti iz okolja, zato morate ustvariti in konfigurirati tudi datoteko .env

.env

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

Vso kodo si lahko ogledate na github.

Mikrostoritve v C++. Fikcija ali resničnost?

In zdaj prihaja zadnja faza pisanja dockerfile in docker-compose.yml. Če sem iskren, je to vzelo večino časa, pa ne samo zato, ker sem noob, ker je bilo treba vsakič znova zgraditi knjižnice, ampak zaradi pasti conana. Na primer, da bi conan prenesel, namestil in zgradil potrebne odvisnosti, ni dovolj, da prenese »conan install .«, temveč mora posredovati tudi parameter -s compiler.libcxx=libstdc++11, sicer tvegate, da boste v fazi povezovanja vaše aplikacije dobili kup napak. Ta napaka se mi pojavlja že nekaj ur in upam, da bo ta članek pomagal drugim rešiti to težavo v krajšem času.

Nato sem po pisanju docker-compose.yml po nasvetu prijatelja dodal podporo model za piškote in zdaj si lahko zagotovite popolno predlogo za storitev REST API v C++, s prilagojenim okoljem in nameščenim PostgreSQL, tako da preprosto vnesete »cookiecutter« v konzolo https://github.com/KovalevVasiliy/cpp_rest_api_template.git" In nato »docker-compose up —build«.

Upam, da bo ta predloga pomagala začetnikom na njihovi težki poti razvijanja aplikacij REST API v odličnem in močnem, a tako okornem jeziku, kot je C++.
Prav tako toplo priporočam branje tukaj to Članek. Podrobneje pojasnjuje, kako delati s POCO in napisati svojo lastno storitev REST API.

Vir: www.habr.com

Dodaj komentar