
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çasCMakeLists.txtinclude(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();
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.TemplateRequestHandlerFactoryclass 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.TemplateServerAppHandlerclass 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.envDATABASE_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 .