Microservices in C++. Fictie of realiteit?

Microservices in C++. Fictie of realiteit?

In dit artikel zal ik vertellen hoe ik een sjabloon (cookiecutter) heb gemaakt en een omgeving heb opgezet voor het schrijven van een REST API-service in C++ met behulp van docker/docker-compose en de conan pakketbeheerder.

Tijdens de volgende hackathon, waaraan ik als backend-ontwikkelaar deelnam, rees de vraag waarmee ik de volgende microservice zou schrijven. Alles wat tot nu toe is geschreven, is door mij en mijn kameraad in Python, aangezien mijn collega een expert op dit gebied was en professioneel backends ontwikkelde, terwijl ik over het algemeen een embedded systeemontwikkelaar was en in het geweldige en vreselijke C++ schreef, en ik Python net op de universiteit leerde.

We werden dus geconfronteerd met de taak om een ​​service met hoge belasting te schrijven, waarvan de belangrijkste taak was om de gegevens die ernaartoe kwamen voor te verwerken en naar de database te schrijven. En na weer een rookpauze stelde een vriend voor dat ik, als C++-ontwikkelaar, deze service met behulp van de professionals zou schrijven. Dit argument is dat het sneller en productiever zal zijn, en dat de jury over het algemeen blij zal zijn met de manier waarop we weten hoe we de middelen van het team moeten beheren. Waarop ik antwoordde dat ik nog nooit zulke dingen in C++ had gedaan en de overige 20+ uur gemakkelijk kon besteden aan het zoeken, samenstellen en koppelen van geschikte bibliotheken. Simpel gezegd, ik kreeg kippenvel. Dat hebben we besloten en alles rustig in Python afgerond.

Nu, tijdens de gedwongen zelfisolatie, besloot ik uit te zoeken hoe ik services in C++ kon schrijven. Het eerste dat u moest doen, was beslissen over een geschikte bibliotheek. Mijn keuze viel op LITTLE, omdat het in een objectgeoriënteerde stijl was geschreven en ook over normale documentatie beschikte. Ook rees de vraag over het kiezen van een montagesysteem. Tot nu toe heb ik alleen met Visual Studio, IAR en bare makefiles gewerkt. En geen van deze systemen sprak mij aan, aangezien ik van plan was de hele dienst in een dockercontainer te laten draaien. Toen besloot ik te proberen cmake en een interessante pakketbeheerder te achterhalen conan. Met deze pakketbeheerder kunt u alle afhankelijkheden in één bestand registreren

conanbestand.txt
[vereist]poco/1.9.3
libpq/11.5

[generatoren] cmake

en met een eenvoudig commando "conan install." installeer de benodigde bibliotheken. Uiteraard was het ook nodig om daar wijzigingen in aan te brengen

CMakeLists.txt

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

Daarna ging ik op zoek naar een bibliotheek om met PostgreSQL te werken, omdat ik daar weinig ervaring mee had, en het was ook de bibliotheek waarmee onze Python-services communiceerden. En weet je wat ik heb geleerd? Het is in POCO! Maar Conan weet niet dat het in POCO zit en weet niet hoe hij het moet bouwen; er staat een verouderd configuratiebestand in de repository (ik heb al over deze fout geschreven aan de makers van POCO). Dit betekent dat je op zoek moet naar een andere bibliotheek.

En toen viel mijn keuze op een minder populaire bibliotheek libpg. En ik had ongelooflijk veel geluk, het was al in Conan en werd zelfs in elkaar gezet en in elkaar gezet.

De volgende stap was het schrijven van een servicesjabloon die aanvragen kan verwerken.
We moeten onze TemplateServerApp-klasse overnemen van Poco::Util::ServerApplication en de hoofdmethode overschrijven.

SjabloonServerApp

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

In de hoofdmethode moeten we de parameters instellen: poort, aantal threads en wachtrijgrootte. En het allerbelangrijkste: u moet een handler opgeven voor inkomende verzoeken. Dit gebeurt door het creëren van een fabriek

TemplateRequestHandlerFactory

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

In mijn geval wordt eenvoudigweg elke keer dezelfde handler aangemaakt: TemplateServerAppHandler. Dit is waar we onze bedrijfslogica kunnen plaatsen.

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

Ik heb ook een klassensjabloon gemaakt om met PostgreSQL te werken. Om eenvoudige SQL uit te voeren, zoals het maken van een tabel, is er een methode ExecuteSQL(). Voor complexere vragen of het ophalen van gegevens moet u een verbinding via GetVerbinding() en gebruik de libpg-API. (Misschien zal ik dit onrecht later corrigeren).

Database

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

Alle parameters voor verbinding met de database worden uit de omgeving gehaald, dus u moet ook het .env-bestand maken en configureren

.env

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

Je kunt alle code bekijken op github.

Microservices in C++. Fictie of realiteit?

En nu komt de laatste fase van het schrijven van de dockerfile en docker-compose.yml. Eerlijk gezegd kostte dit het grootste deel van de tijd, en niet alleen omdat ik een noob ben, omdat het nodig was om de bibliotheken elke keer opnieuw op te bouwen, maar vanwege de valkuilen van conan. Om conan bijvoorbeeld de noodzakelijke afhankelijkheden te laten downloaden, installeren en bouwen, is het niet voldoende dat het “conan install .” downloadt, het moet ook de parameter -s compiler.libcxx=libstdc++11 doorgeven, anders u loopt het risico een heleboel fouten te krijgen in de koppelingsfase van uw toepassing. Ik zit al enkele uren met deze fout vast en ik hoop dat dit artikel andere mensen zal helpen dit probleem in minder tijd op te lossen.

Vervolgens heb ik, na het schrijven van docker-compose.yml, op advies van mijn vriend, ondersteuning toegevoegd koekjesvormer en nu kun je een volwaardige sjabloon krijgen voor een REST API-service in C++, met een aangepaste omgeving en PostgreSQL geïnstalleerd, simpelweg door "cookiecutter" in de console in te voeren https://github.com/KovalevVasiliy/cpp_rest_api_template.git" En dan "docker-compose up -build".

Ik hoop dat dit sjabloon beginners zal helpen op hun moeilijke pad van het ontwikkelen van REST API-applicaties in de geweldige en krachtige, maar zo'n onhandige taal als C++.
Verder raad ik je ten zeerste aan om hier te lezen deze artikel. Er wordt gedetailleerder uitgelegd hoe u met POCO kunt werken en uw eigen REST API-service kunt schrijven.

Bron: www.habr.com

Voeg een reactie