Microservices in C ++. Fiktion oder Realität?



In diesem Artikel werde ich darüber sprechen, wie ich einen Cookie-Cutter erstellt und eine Umgebung zum Schreiben eines REST-API-Dienstes in C ++ mit Docker / Docker-Compose und dem Conan-Paket-Manager eingerichtet habe.


Während des nächsten Hackathons, an dem ich als Backend-Entwickler teilgenommen habe, stellte sich die Frage, worüber ich den nächsten Microservice schreiben soll. Alles, was im Moment geschrieben wurde, wurde von mir und meinem Freund in Python geschrieben, da mein Kollege ein Experte auf diesem Gebiet war und professionell an der Entwicklung von Backends beteiligt war, während ich im Allgemeinen Entwickler für eingebettete Systeme war und in großartigem und schrecklichem C schrieb ++ und Python haben gerade an der Universität gelernt.


Wir standen also vor der Aufgabe, einen hoch geladenen Dienst zu schreiben, dessen Hauptaufgabe darin bestand, die eingehenden Daten vorzuverarbeiten und in die Datenbank zu schreiben. Und nach einer weiteren Rauchpause schlug ein Freund vor, dass ich als C ++ - Entwickler diesen Service auf die Pluspunkte schreibe. Dies mit der Tatsache zu argumentieren, dass es schneller und produktiver sein wird, und in der Tat wird die Jury begeistert sein, wie wir die Ressourcen des Teams verwalten können. Darauf antwortete ich, dass ich solche Dinge in C ++ noch nie gemacht habe und die verbleibenden mehr als 20 Stunden problemlos für das Finden, Kompilieren und Verknüpfen geeigneter Bibliotheken verwenden könnte. Einfach gesagt, ich hatte Angst. Das haben sie beschlossen und Python ruhig alles hinzugefügt.


, , ++. , , . POCO, - , . , . Visual Studio, IAR «» makefile. , docker-. cmake conan.

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

[generators]
cmake

und mit dem einfachen Befehl "conan install". Installieren Sie die erforderlichen Bibliotheken. Natürlich waren auch Änderungen in erforderlich

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

Danach suchte ich nach einer Bibliothek für die Arbeit mit PostgreSQL, da ich damit wenig Erfahrung hatte und unsere Python-Dienste damit interagierten. Und weißt du was ich herausgefunden habe? Sie ist in POCO! Conan weiß jedoch nicht, dass es sich um POCO handelt, und weiß nicht, wie es erstellt werden soll. Die veraltete Konfigurationsdatei befindet sich im Repository (ich habe den Erstellern von POCO bereits über diesen Fehler geschrieben). Sie müssen also nach einer anderen Bibliothek suchen.

Und dann fiel meine Wahl auf die weniger beliebte libpg- Bibliothek . Und ich hatte unglaublich viel Glück, sie war bereits in Conan und sogar zusammengebaut und zusammengebaut.

Der nächste Schritt bestand darin, eine Dienstvorlage zu schreiben, die Anforderungen verarbeiten kann.
Wir müssen unsere TemplateServerApp-Klasse von Poco :: Util :: ServerApplication erben und die Hauptmethode überschreiben.

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


In der Hauptmethode müssen wir die Parameter einstellen: Port, Anzahl der Threads und Warteschlangengröße. Und am wichtigsten ist, dass Sie einen Handler für eingehende Anforderungen angeben. Dies geschieht durch die Schaffung einer Fabrik.

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


In meinem Fall wird jedes Mal derselbe Handler erstellt - TemplateServerAppHandler. Hier können wir unsere Geschäftslogik positionieren.

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

Ich habe auch eine Klassenvorlage für die Arbeit mit PostgreSQL erstellt. Um einfaches SQL auszuführen, beispielsweise um eine Tabelle zu erstellen, gibt es eine ExecuteSQL () -Methode . Für komplexere Abfragen oder das Abrufen von Daten müssen Sie eine Verbindung über GetConnection () herstellen und die libpg-API verwenden. (Vielleicht werde ich dann diese Ungerechtigkeit korrigieren).

Datenbank
#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 Parameter für die Verbindung zur Datenbank stammen aus der Umgebung. Sie müssen daher auch die ENV-Datei erstellen und konfigurieren

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


Sie können den gesamten Code auf dem Github sehen.



dockerfile docker-compose.yml. , , , , , - conan. , , conan , , «conan install .», -s compiler.libcxx=libstdc++11, . , , .


, docker-compose.yml, cookiecutter REST API ++, c , PostgreSQL, «cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git». «docker-compose up --build».


, REST API , , ++.
, . POCO REST API .


All Articles