Mikrotjenester i C++. Fiksjon eller virkelighet?

Mikrotjenester i C++. Fiksjon eller virkelighet?

I denne artikkelen vil jeg snakke om hvordan jeg opprettet en mal (cookiecutter) og satte opp et miljø for å skrive en REST API-tjeneste i C++ ved hjelp av docker/docker-compose og conan-pakkebehandlingen.

Under neste hackathon, der jeg deltok som backend-utvikler, dukket spørsmålet opp om hva jeg skulle bruke for å skrive neste mikrotjeneste. Alt som er skrevet så langt er skrevet av meg og min kamerat i Python, siden min kollega var en ekspert på dette feltet og profesjonelt utviklet backends, mens jeg generelt var en utvikler av innebygde systemer og skrev i den flotte og forferdelige C++, og jeg har nettopp lært Python på universitetet.

Så vi sto overfor oppgaven med å skrive en høybelastningstjeneste, hvis hovedoppgave var å forhåndsbehandle dataene som kom til den og skrive dem til databasen. Og etter nok en røykpause, foreslo en venn at jeg som C++-utvikler skulle skrive denne tjenesten ved å bruke proffene. Dette argumenterer for at det vil være raskere, mer produktivt, og generelt sett vil juryen være fornøyd med hvordan vi vet hvordan vi skal forvalte teamets ressurser. Til det svarte jeg at jeg aldri hadde gjort slike ting i C++ og lett kunne bruke de resterende 20+ timene til å søke, kompilere og koble sammen passende biblioteker. Enkelt sagt, jeg chicken out. Det var det vi bestemte oss for og rolig fullførte alt i Python.

Nå, under den tvungne selvisolasjonen, bestemte jeg meg for å finne ut hvordan jeg skulle skrive tjenester i C++. Det første du måtte gjøre var å velge et passende bibliotek. Valget mitt falt på POCO, siden den ble skrevet i en objektorientert stil og også skilte med normal dokumentasjon. Spørsmålet dukket også opp om valg av monteringssystem. Frem til dette punktet har jeg bare jobbet med Visual Studio, IAR og bare make-filer. Og ingen av disse systemene appellerte til meg, siden jeg planla å kjøre hele tjenesten i en docker-container. Så bestemte jeg meg for å prøve å finne ut cmake og en interessant pakkebehandler conan. Denne pakkebehandlingen tillot deg å registrere alle avhengigheter i én fil

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

[generatorer] cmake

og med en enkel kommando "conan install ." installere de nødvendige bibliotekene. Det var naturligvis også nødvendig å gjøre endringer i

CMakeLists.txt

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

Etter det begynte jeg å se etter et bibliotek for å jobbe med PostgreSQL, siden det var det jeg hadde liten erfaring med å jobbe med, og det var også det som Python-tjenestene våre samhandlet med. Og vet du hva jeg lærte? Det er i POCO! Men conan vet ikke at den er i POCO og vet ikke hvordan den skal bygges; det er en utdatert konfigurasjonsfil i depotet (jeg har allerede skrevet om denne feilen til skaperne av POCO). Dette betyr at du må se etter et annet bibliotek.

Og så falt valget mitt på et mindre populært bibliotek libpg. Og jeg var utrolig heldig, den var allerede i conan og ble til og med satt sammen og satt sammen.

Neste steg var å skrive en tjenestemal som kan behandle forespørsler.
Vi må arve TemplateServerApp-klassen vår fra Poco::Util::ServerApplication og overstyre hovedmetoden.

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

I hovedmetoden må vi angi parameterne: port, antall tråder og køstørrelse. Og viktigst av alt, du må spesifisere en behandler for innkommende forespørsler. Dette gjøres ved å lage en fabrikk

TemplateRequestHandlerFactory

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

I mitt tilfelle skaper den ganske enkelt den samme behandleren hver gang - TemplateServerAppHandler. Det er her vi kan plassere forretningslogikken vår.

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

Jeg har også laget en klassemal for å jobbe med PostgreSQL. For å utføre enkel SQL, som å lage en tabell, finnes det en metode ExecuteSQL(). For mer komplekse forespørsler eller datainnhenting må du få en tilkobling via GetConnection() og bruk libpg API. (Kanskje senere vil jeg rette opp denne urettferdigheten).

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 parametere for å koble til databasen er hentet fra miljøet, så du må også opprette og konfigurere .env-filen

.og V

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

Du kan se all koden på github.

Mikrotjenester i C++. Fiksjon eller virkelighet?

Og nå kommer det siste stadiet med å skrive dockerfilen og docker-compose.yml. For å være ærlig tok dette mesteparten av tiden, og ikke bare fordi jeg er en noob, fordi det var nødvendig å gjenoppbygge bibliotekene hver gang, men på grunn av fallgruvene til conan. For eksempel, for at conan skal laste ned, installere og bygge de nødvendige avhengighetene, er det ikke nok for det å laste ned “conan install .”, det må også sende parameteren -s compiler.libcxx=libstdc++11, ellers du risikerer å få en haug med feil på koblingsstadiet din søknad. Jeg har sittet fast med denne feilen i flere timer, og jeg håper denne artikkelen vil hjelpe andre med å løse dette problemet på kortere tid.

Deretter, etter å ha skrevet docker-compose.yml, etter råd fra min venn, la jeg til støtte pepperkakeform og nå kan du få deg en fullverdig mal for en REST API-tjeneste i C++, med et tilpasset miljø, og PostgreSQL installert, ganske enkelt ved å skrive inn "cookiecutter" i konsollen https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Og så "docker-komponer opp - bygg".

Jeg håper denne malen vil hjelpe nybegynnere på deres vanskelige vei med å utvikle REST API-applikasjoner i det flotte og kraftige, men så klønete språket som C++.
Dessuten anbefaler jeg på det sterkeste å lese her dette artikkel. Den forklarer mer detaljert hvordan du jobber med POCO og skriver din egen REST API-tjeneste.

Kilde: www.habr.com

Legg til en kommentar