Microservices en C ++. Fiction ou réalité?



Dans cet article, je vais parler de la façon dont j'ai créé un cookiecutter et mis en place un environnement pour écrire un service API REST en C ++ en utilisant docker / docker-compose et le gestionnaire de packages conan.


Lors du prochain hackathon, auquel j'ai participé en tant que développeur backend, la question s'est posée de savoir sur quoi écrire le prochain microservice. Tout ce qui a été écrit en ce moment a été écrit par moi et mon ami en Python, car mon collègue était un expert dans ce domaine et était professionnellement impliqué dans le développement de backends, alors que j'étais généralement développeur de systèmes embarqués et écrit en grand et terrible C ++, et Python vient d'apprendre à l'université.


Nous avons donc été confrontés à la tâche d'écrire un service très chargé, dont la tâche principale était le prétraitement des données qui y arrivaient et leur écriture dans la base de données. Et après une autre pause fumée, un ami m'a suggéré, en tant que développeur C ++, d'écrire ce service sur les avantages. Arguant de cela en disant que ce sera plus rapide, plus productif, et en effet, le jury sera ravi de la façon dont nous pouvons gérer les ressources de l'équipe. À quoi j'ai répondu que je n'avais jamais fait de telles choses en C ++ et que je pouvais facilement consacrer les 20+ heures restantes à trouver, compiler et lier des bibliothèques appropriées. Autrement dit, j'ai eu peur. C'est ce qu'ils ont décidé et ont calmement ajouté tout à Python.


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

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

[generators]
cmake

et avec la simple commande «conan install». installez les bibliothèques nécessaires. Naturellement, des changements étaient également

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

Après cela, j'ai commencé à chercher une bibliothèque pour travailler avec PostgreSQL, car c'est avec elle que j'avais peu d'expérience, et c'est avec elle que nos services Python ont interagi. Et tu sais ce que j'ai découvert? Elle est à POCO! Mais conan ne sait pas qu'il est dans POCO et ne peut pas le construire, le fichier de configuration obsolète est dans le référentiel (j'ai déjà écrit sur cette erreur aux créateurs de POCO). Donc, vous devez chercher une autre bibliothèque.

Et puis mon choix s'est porté sur la bibliothèque libpg moins populaire . Et j'ai eu une chance incroyable, elle était déjà en conan et même assemblée et assemblée.

L'étape suivante consistait à écrire un modèle de service capable de gérer les demandes.
Nous devons hériter notre classe TemplateServerApp de Poco :: Util :: ServerApplication et remplacer la méthode principale.

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


Dans la méthode principale, nous devons définir les paramètres: port, nombre de threads et taille de la file d'attente. Et surtout, vous devez spécifier un gestionnaire de demandes entrantes. Cela se fait par la création d'une usine.

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


Dans mon cas, il crée simplement le même gestionnaire à chaque fois - TemplateServerAppHandler. C'est ici que nous pouvons positionner notre logique métier.

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

J'ai également créé un modèle de classe pour travailler avec PostgreSQL. Afin d'exécuter du SQL simple, par exemple pour créer une table, il existe une méthode ExecuteSQL () . Pour les requêtes plus complexes ou la récupération de données, vous devrez vous connecter via GetConnection () et utiliser l'API libpg. (Peut-être alors je corrigerai cette injustice).

Base de données
#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;
};

Tous les paramètres de connexion à la base de données proviennent de l'environnement, vous devez donc également créer et configurer le fichier .env

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


Vous pouvez voir tout le code sur le github.



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