Microsserviços em C ++. Ficção ou realidade?



Neste artigo, falarei sobre como criei um cookiecutter e configurei o ambiente para escrever um serviço de API REST em C ++ usando docker / docker-compose e o gerenciador de pacotes conan.


Durante o próximo hackathon, no qual participei como desenvolvedor de back-end, surgiu a pergunta sobre o que escrever no próximo microsserviço. Tudo o que foi escrito no momento foi escrito por mim e meu amigo em Python, pois meu colega era especialista neste campo e estava profissionalmente envolvido no desenvolvimento de back-end, enquanto eu geralmente era desenvolvedor de sistemas embarcados e escrevia em grande e terrível C ++ e Python acabaram de aprender na universidade.


Portanto, enfrentamos a tarefa de escrever um serviço altamente carregado, cuja principal tarefa era o pré-processamento dos dados que chegavam a ele e a gravação no banco de dados. E após outra pausa para fumar, um amigo sugeriu que eu, como desenvolvedor de C ++, escrevesse esse serviço nas vantagens. Argumentando isso com o fato de que será mais rápido, mais produtivo e, de fato, o júri ficará encantado com a forma como podemos gerenciar os recursos da equipe. Ao qual respondi que nunca havia feito isso em C ++ e poderia dedicar facilmente as mais de 20 horas restantes à busca, compilação e vinculação de bibliotecas adequadas. Simplificando, fiquei com medo. Foi isso que eles decidiram e calmamente adicionaram tudo ao Python.


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

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

[generators]
cmake

e com o simples comando "conan install". instale as bibliotecas necessárias. Naturalmente, também foram necessárias mudanças

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

Depois disso, comecei a procurar uma biblioteca para trabalhar com o PostgreSQL, pois era com ela que tinha pouca experiência e era com isso que nossos serviços Python interagiam. E você sabe o que eu descobri? Ela está no POCO! Mas conan não sabe que está no POCO e não pode construí-lo, o arquivo de configuração obsoleto está no repositório (eu já escrevi sobre esse erro para os criadores do POCO). Então, você precisa procurar outra biblioteca.

E então minha escolha caiu na biblioteca libpg menos popular . E eu tive uma sorte incrível, ela já estava em Conan e até montada e montada.

O próximo passo foi escrever um modelo de serviço que possa lidar com solicitações.
Devemos herdar nossa classe TemplateServerApp de Poco :: Util :: ServerApplication e substituir o 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;
}


No método principal, devemos definir os parâmetros: porta, número de threads e tamanho da fila. E o mais importante, você deve especificar um manipulador de solicitações recebidas. Isso é feito através da criação de uma fábrica.

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


No meu caso, ele apenas cria o mesmo manipulador toda vez - TemplateServerAppHandler. É aqui que podemos posicionar nossa lógica de negócios.

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

Também criei um modelo de classe para trabalhar com o PostgreSQL. Para executar SQL simples, por exemplo, para criar uma tabela, existe um método ExecuteSQL () . Para consultas mais complexas ou recuperação de dados, você precisará obter conexão via GetConnection () e usar a API libpg. (Talvez então eu corrija essa injustiça).

Base de dados
#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 os parâmetros para conectar-se ao banco de dados são obtidos do ambiente, portanto, você também precisa criar e configurar o arquivo .env

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


Você pode ver todo o código no 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