Mikroservoj en C++. Fikcio aŭ realeco?

Mikroservoj en C++. Fikcio aŭ realeco?

En ĉi tiu artikolo mi parolos pri kiel mi kreis ŝablonon (cookiecutter) kaj starigis medion por verki REST-API-servon en C++ uzante docker/docker-compose kaj la pakaĵadministrilon conan.

Dum la sekva hackathon, en kiu mi partoprenis kiel backend-programisto, la demando ekestis pri kion uzi por skribi la sekvan mikroservon. Ĉio, kio estis skribita ĝis nun, estis skribita de mi kaj mia kamarado en Python, ĉar mia kolego estis fakulo pri ĉi tiu fako kaj profesie evoluigis backends, dum mi ĝenerale estis enigita sistemo-programisto kaj skribis en la bonega kaj terura C++, kaj mi ĵus lernis Python en universitato.

Do, ni alfrontis la taskon verki altŝarĝan servon, kies ĉefa tasko estis antaŭprilabori la datumojn venantajn al ĝi kaj skribi ĝin al la datumbazo. Kaj post alia fumpaŭzo, amiko sugestis, ke mi, kiel C++-programisto, verku ĉi tiun servon uzante la avantaĝojn. Argumentante ĉi tion estas, ke ĝi estos pli rapida, pli produktiva, kaj ĝenerale, la ĵurio ĝojos pri kiel ni scias kiel administri la rimedojn de la teamo. Al kio mi respondis, ke mi neniam faris tiajn aferojn en C++ kaj povas facile dediĉi la ceterajn 20+ horojn al serĉado, kompilo kaj ligado de taŭgaj bibliotekoj. Simple dirite, mi kokidiĝis. Tion ni decidis kaj trankvile kompletigis ĉion en Python.

Nun, dum la devigita mem-izolado, mi decidis eltrovi kiel skribi servojn en C++. La unua afero estis decidi pri taŭga biblioteko. Mia elekto falis poco, ĉar ĝi estis verkita en objekto-orientita stilo kaj ankaŭ fanfaronis pri normala dokumentado. Ankaŭ, la demando ekestis pri elektado de asembleosistemo. Ĝis ĉi tiu punkto mi nur laboris kun Visual Studio, IAR kaj nudaj makedosieroj. Kaj neniu el ĉi tiuj sistemoj allogis min, ĉar mi planis funkciigi la tutan servon en docker-ujo. Tiam mi decidis provi eltrovi cmake kaj interesan pakaĵadministrilon conan. Ĉi tiu pakaĵadministrilo permesis al vi registri ĉiujn dependecojn en unu dosiero

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

[generatoroj] cmake

kaj per simpla komando "conan install ." instali la necesajn bibliotekojn. Nature, ankaŭ necesis fari ŝanĝojn al

CMakeLists.txt

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

Post tio, mi komencis serĉi bibliotekon por labori kun PostgreSQL, ĉar ĝi estis tiu kun kiu mi havis malmulte da sperto laboranta, kaj ĝi ankaŭ estis tiu kun kiu niaj Python-servoj interagis. Kaj ĉu vi scias, kion mi lernis? Ĝi estas en POCO! Sed conan ne scias, ke ĝi estas en POCO kaj ne scias kiel konstrui ĝin; estas malaktuala agorda dosiero en la deponejo (mi jam skribis pri ĉi tiu eraro al la kreintoj de POCO). Ĉi tio signifas, ke vi devos serĉi alian bibliotekon.

Kaj tiam mia elekto falis sur malpli popularan bibliotekon libpg. Kaj mi estis nekredeble bonŝanca, ĝi jam estis en konano kaj eĉ estis kunmetita kaj kunvenita.

La sekva paŝo estis verki servoŝablonon, kiu povas procesi petojn.
Ni devas heredi nian TemplateServerApp klason de Poco::Util::ServerApplication kaj superregi la ĉefan metodon.

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

En la ĉefa metodo ni devas agordi la parametrojn: haveno, nombro da fadenoj kaj vostograndeco. Kaj plej grave, vi devas specifi prizorganton por envenantaj petoj. Ĉi tio estas farita kreante fabrikon

TemplateRequestHandlerFactory

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

En mia kazo, ĝi simple kreas la saman prizorganton ĉiufoje - TemplateServerAppHandler. Ĉi tie ni povas meti nian komercan logikon.

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

Mi ankaŭ kreis klasŝablonon por labori kun PostgreSQL. Por plenumi simplan SQL, kiel krei tabelon, ekzistas metodo ExecuteSQL (). Por pli kompleksaj demandoj aŭ reakiro de datumoj, vi devos akiri konekton per GetConnection () kaj uzu la libpg API. (Eble poste mi korektos ĉi tiun maljustaĵon).

Datumbazo

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

Ĉiuj parametroj por konektiĝi al la datumbazo estas prenitaj el la medio, do vi ankaŭ devas krei kaj agordi la .env-dosieron

.env

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

Vi povas vidi la tutan kodon ĉe github.

Mikroservoj en C++. Fikcio aŭ realeco?

Kaj nun venas la fina etapo de verkado de la dockerfile kaj docker-compose.yml. Verdire, ĉi tio daŭris la plej grandan parton de la tempo, kaj ne nur ĉar mi estas novulo, ĉar necesis ĉiufoje rekonstrui la bibliotekojn, sed pro la faŭltoj de konano. Ekzemple, por ke conan elŝutu, instalu kaj konstruu la necesajn dependecojn, ne sufiĉas ke ĝi elŝutu "conan install .", ĝi ankaŭ bezonas pasi la parametron -s compiler.libcxx=libstdc++11, alie. vi riskas ricevi multajn erarojn ĉe la ligofazo de via aplikaĵo. Mi restis kun ĉi tiu eraro dum pluraj horoj kaj mi esperas, ke ĉi tiu artikolo helpos aliajn homojn solvi ĉi tiun problemon en malpli da tempo.

Poste, skribinte docker-compose.yml, laŭ la konsilo de mia amiko, mi aldonis subtenon kuketo kaj nun vi povas akiri al vi plentaŭgan ŝablonon por REST API-servo en C++, kun personigita medio, kaj PostgreSQL instalita, simple enirante "cookiecutter" en la konzolon. https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Kaj tiam "docker-compose up -build".

Mi esperas, ke ĉi tiu ŝablono helpos komencantojn sur ilia malfacila vojo evoluigi REST-API-aplikaĵojn en la bonega kaj potenca, sed tiel mallerta lingvo kiel C++.
Ankaŭ mi tre rekomendas legi ĉi tie ĉi tio artikolo. Ĝi klarigas pli detale kiel labori kun POCO kaj verki vian propran REST API-servon.

fonto: www.habr.com

Aldoni komenton