Microservicios en C ++. ¿Ficción o realidad?



En este artículo, hablaré sobre cómo creé un cookiecutter y configuré un entorno para escribir un servicio API REST en C ++ usando docker / docker-compose y el administrador de paquetes conan.


Durante el siguiente hackathon, en el que participé como desarrollador de back-end, surgió la pregunta sobre en qué escribir el próximo microservicio. Todo lo que estaba escrito en este momento fue escrito por mí y mi amigo en Python, ya que mi colega era un experto en este campo y estaba involucrado profesionalmente en el desarrollo de backends, mientras que generalmente era un desarrollador de sistemas integrados y escribía en excelente y terrible C ++, y Python acaba de aprender en la universidad.


Entonces, nos enfrentamos a la tarea de escribir un servicio altamente cargado, cuya tarea principal era el preprocesamiento de los datos que llegaban y los escribía en la base de datos. Y después de otro descanso para fumar, un amigo sugirió que yo, como desarrollador de C ++, escribiera este servicio en las ventajas. Argumentando esto con el hecho de que será más rápido, más productivo y, de hecho, el jurado estará encantado con la forma en que podemos administrar los recursos del equipo. A lo que respondí que nunca había hecho tales cosas en C ++ y que podría dedicar fácilmente las más de 20 horas restantes a encontrar, compilar y vincular bibliotecas adecuadas. En pocas palabras, me asusté. Eso es lo que decidieron y calmadamente agregaron todo a Python.


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

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

[generators]
cmake

y con el simple comando "conan install". Instale las bibliotecas necesarias. Naturalmente, también se requirieron cambios en

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

Después de eso, comencé a buscar una biblioteca para trabajar con PostgreSQL, ya que era con ella que tenía poca experiencia, y fue con ella que interactuaron nuestros servicios de Python. ¿Y sabes lo que descubrí? Ella está en POCO! Pero conan no sabe que está en POCO y no sabe cómo construirlo, el archivo de configuración obsoleto está en el repositorio (ya escribí sobre este error a los creadores de POCO). Entonces, tienes que buscar otra biblioteca.

Y luego mi elección recayó en la biblioteca libpg menos popular . Y tuve una suerte increíble, ella ya estaba en Conan e incluso se reunió y se reunió.

El siguiente paso fue escribir una plantilla de servicio que pueda manejar solicitudes.
Debemos heredar nuestra clase TemplateServerApp de Poco :: Util :: ServerApplication y anular el método principal.

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


En el método principal, debemos establecer los parámetros: puerto, número de subprocesos y tamaño de la cola. Y lo más importante, debe especificar un controlador de solicitud entrante. Esto se hace mediante la creación de una fábrica.

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


En mi caso, solo crea el mismo controlador cada vez: TemplateServerAppHandler. Es aquí donde podemos posicionar nuestra lógica de negocios.

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

También creé una plantilla de clase para trabajar con PostgreSQL. Para ejecutar SQL simple, por ejemplo para crear una tabla, hay un método ExecuteSQL () . Para consultas más complejas o recuperación de datos, tendrá que obtener la conexión a través de GetConnection () y usar la API libpg. (Quizás entonces corregiré esta injusticia).

Base de datos
#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;
};

Todos los parámetros para conectarse a la base de datos se toman del entorno, por lo que también debe crear y configurar el archivo .env

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


Puedes ver todo el código en el 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