Mikroszolgáltatások C++-ban. Képzelet vagy valóság?

Mikroszolgáltatások C++-ban. Képzelet vagy valóság?

Ebben a cikkben arról fogok beszélni, hogyan készítettem el egy sablont (cookiecutter), és hogyan állítottam be a környezetet egy C++-os REST API szolgáltatás írásához, Docker / Docker-Compose és a Conan csomagkezelő használatával.

Egy hackathon során, ahol backend fejlesztőként részt vettem, felmerült a kérdés, hogy mivel írjuk meg a következő mikroszervizt. Minden eddig írt kódot én és a barátom Python nyelven írtunk, mivel a kollégám szakértő volt ezen a területen, és professzionálisan foglalkozott a backend fejlesztésével, míg én egyébként beágyazott rendszerek fejlesztője voltam, és a nagyszerű és borzalmas C++-t használtam, míg a Python-t csak az egyetemen tanultam.

Tehát a feladatunk az volt, hogy egy nagy terhelésre tervezett szolgáltatást írjunk, amelynek fő feladata a beérkező adatok előfeldolgozása és azok elmentése az adatbázisba volt. A következő szünetben a barátom javasolta, hogy C++ fejlesztőként én írjam ezt a szolgáltatást C++-ban. Az érvet azzal támasztotta alá, hogy így gyorsabb és hatékonyabb lesz, és a zsűri is el lesz ragadtatva, hogy milyen jól bántunk a csapat erőforrásaival. Erre én azt válaszoltam, hogy soha nem foglalkoztam hasonlókkal C++-ban, és könnyedén eltölthetem a hátralévő 20+ órát a megfelelő könyvtárak keresésével, fordításával és összekapcsolásával. Egyszerűbben fogalmazva, megijedtem. Ezen a ponton abban maradtunk, és nyugodtan befejeztük az egészet Pythonban.

Jelenleg, a kényszerű önizolálás időszakában úgy döntöttem, hogy megtanulom, hogyan lehet C++-ban szolgáltatásokat írni. Az első dolog, amit meg kellett tennem, az volt, hogy kiválasszak egy megfelelő könyvtárat. Az én választásom a POCO, mivel ez objektumorientált stílusban készült, és jó dokumentációval büszkélkedhetett. Emellett felmerült a kérdés az építési rendszer kiválasztásáról is. Eddig csak a Visual Studio-val, IAR-rel és a „meztelen” makefile-kkal dolgoztam. Egyik rendszer sem vonzott különösebben, mivel azt terveztem, hogy az egész szolgáltatást Docker konténerben futtatom. Így döntöttem úgy, hogy próbálkozom a cmake és egy érdekes csomagkezelővel, a conanhasználatával. Ez a csomagkezelő lehetővé tette, hogy minden függőséget egy fájlban rögzítsek,

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

[generators]
cmake

és egy egyszerű "conan install ." parancs segítségével telepítsem a szükséges könyvtárakat. Természetesen a

CMakeLists.txt-en is módosításokat kellett végezni.

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

Ezután elkezdtem keresni egy könyvtárat a PostgreSQL-hez, mert volt egy kis tapasztalatom vele, és a szolgáltatásaink is Pythonban kommunikáltak vele. És tudja, mit tudtam meg? Ott van a POCO-ban! De a conan nem tud róla, hogy létezik a POCO-ban, és nem tudja megépíteni, a repóban egy elavult konfigurációs fájl található (már írtam erről a hibáról a POCO készítőinek). Ezért másik könyvtárat kell keresnem.

Így a választásom egy kevésbé népszerű könyvtárra esett. libpg. És hihetetlenül szerencsés voltam, mert már benne volt a conan-ban, és még össze is lehetett szerelni.

A következő lépés a kéréskezelő szolgáltatás sablonjának megírása volt.
Örökölnünk kell a TemplateServerApp osztályt a Poco::Util::ServerApplication-ból, és felül kell írni a main metódust.

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

A main metódusban meg kell adnunk a paramétereket: port, szálak száma és a sor mérete. A legfontosabb, hogy meg kell adnunk a bejövő igények kezelőjét. Ezt úgy tesszük, hogy létrehozunk egy gyárat.

TemplateRequestHandlerFactory

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

Az én esetemben ez egyszerűen minden alkalommal ugyanazt a kezelőt – a TemplateServerAppHandler-t – hozza létre. Itt tudjuk elhelyezni az üzleti logikánkat.

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

Én is készítettem egy osztály sablont a PostgreSQL használatához. Az egyszerű SQL végrehajtásához, például egy tábla létrehozásához, van egy metódus ExecuteSQL(). Bonyolultabb lekérdezésekhez vagy adatok beszerzéséhez kapcsolatot kell szereznünk a GetConnection() metódussal és a libpg API-ját kell használnunk. (Lehet, hogy ezt a jogtalanságot később orvoslom).

Adatbázis

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

Minden adatbázis-kapcsolati paraméter a környezetből származik, ezért neked is létre kell hoznod és be kell állítanod egy .env fájlt

.env

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

Megnézheted a teljes kódot a GitHub-on.

Mikroszolgáltatások C++-ban. Képzelet vagy valóság?

Elérkeztünk a Dockerfile és a docker-compose.yml írásának utolsó szakaszához. Őszintén szólva, ennek megírása sok időt vett igénybe, nemcsak azért, mert új vagyok, és minden alkalommal újra kellett építeni a könyvtárakat, hanem a conan problémái miatt is. Például ahhoz, hogy a conan letöltse, telepítse és lebuildelje a szükséges függőségeket, nem elég, ha kiadod a „conan install .” parancsot, hanem a -s compiler.libcxx=libstdc++11 paramétert is meg kell adnod, különben rengeteg hibaüzenetet kapsz az alkalmazásod összesítésekor. Ezzel a hibával órákat ültem, és remélem, hogy ez a cikk segít másoknak, hogy ezt a problémát gyorsabban megoldják.

Ezután, a docker-compose.yml megírása után, a barátom tanácsára hozzáadtam a támogatást cookiecutter és most teljes sablont kaphatsz egy C++ REST API szolgáltatáshoz, beállított környezettel és működő PostgreSQL-lal, csupán annyit kell beírnod a konzolba: „cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git”. Aztán „docker-compose up —build”.

Remélem, hogy ez a sablon segíteni fog a kezdőknek a REST API alkalmazások fejlesztésének nehéz útján a nagyszerű és hatalmas, de oly lassú nyelven, mint a C++.
Ezen túlmenően, nagyon ajánlom, hogy olvasd el ezt a erre cikket. Benne részletesebben magyarázzák el, hogyan kell dolgozni a POCO-val és hogyan írhatod meg a saját REST API szolgáltatásodat.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster