
В тази статия ще споделя как създадох шаблон (cookiecutter) и настроих среда за разработка на REST API услуга на C++ с използване на docker/docker-compose и пакетния мениджър conan.
По време на поредния хакатон, в който участвах като бекенд-разработчик, възникна въпросът с какво да напишем следващия микросервис. Всичко, което беше написано дотук, беше мое и на моя на езика Python, тъй като колегата ми беше специалист в тази област и професионално се занимаваше с разработка на бекенди, докато аз всъщност бях разработчик на вградени системи и пишех на великото и ужасно C++, а Python просто го бях изучил в университета.
И така, пред нас се изправи задачата да напишем високо натоварена услуга, чиято основна задача беше предварителна обработка на постъпващите данни и записването им в БД. След поредната почивка, приятелят ми предложи, като C++ разработчик, да напиша тази услуга на плюсове. Аргументирайки, че така ще бъде по-бързо, по-продуктивно и въобще, журито ще бъде впечатлено от това как умеем да управляваме ресурсите на екипа. На което отговорих, че никога не съм се занимавал с такива неща на C++ и с лекота мога да посветя останалите 20+ часа на търсене, компилиране и компоновка на подходящи библиотеки. Инак казано, се уплаших. На това приключихме и спокойно дописахме всичко на Python.
Сега, по време на принудителна самоизолация, реших да разбера как да пиша услуги на C++. Първото, което трябваше да направя, беше да се определи с подходящата библиотека. Изборът ми падна на , тъй като беше написана в обектно-ориентиран стил и можеше да се похвали с нормална документация. Освен това, възникна въпросът за избора на система за компилация. До този момент работех само с Visual Studio, IAR и „голи“ makefile. И нито една от тези системи не ме привличаше, тъй като планирах да стартирам цялата услуга в docker-контейнер. Тогава реших да опитам да се запозная с cmake и интересния пакетен мениджър . Този пакетен мениджър позволяваше да опиша всички зависимости в един файл
conanfile.txt
[изисквания]
poco/1.9.3
libpq/11.5
[генератори]
cmake
и с помощта на простата команда „conan install .“ да инсталирам необходимите библиотеки. Разбира се, трябваше също така да внеса промени в
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
След това започнах да търся библиотека за работа с PostgreSQL, тъй като имах малък опит с нея, а и точно с нея взаимодействат нашите услуги на Python. И знаете ли какво научих? Тя е в POCO! Но conan не знае, че я има в POCO и не може да я построи, в репозитория има остарял конфигурационен файл (вече писах за тази грешка на създателите на POCO). Следователно, ще трябва да търся друга библиотека.
И така, моят избор падна на по-малко популярната библиотека . И имах неописуема късмет, тя вече беше в conan и дори се компилираше и линкваше.
Следващата стъпка беше написването на шаблон на услуга, която може да обработва заявки.
Ние трябва да наследим нашия клас TemplateServerApp от Poco::Util::ServerApplication и да преопределим метода main.
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 &)
{
HTTPServerParams* pParams = new HTTPServerParams;
pParams->setMaxQueued(100);
pParams->setMaxThreads(16);
HTTPServer s(new TemplateRequestHandlerFactory, ServerSocket(8000), pParams);
s.start();
cerr << "Сървърът стартира" << endl;
waitForTerminationRequest(); // изчакайте CTRL-C или убийте
cerr << "Затваряне..." << endl;
s.stop();
return Application::EXIT_OK;
}В метода main трябва да зададем параметрите: порт, брой нишки и размер на опашката. А най-важното е да зададем обработчик на входящи заявки. Това се прави чрез създаване на фабрика
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};В моя случай тя просто всеки път създава един и същи обработчик — TemplateServerAppHandler. Именно тук можем да разположим нашата бизнес логика.
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 << "Метод: " << 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();
}
};Създадох и шаблон на клас за работа с PostgreSQL. За да изпълните просто SQL, например да създадете таблица, има метод ExecuteSQL(). За по-сложни заявки или за получаване на данни ще трябва да вземете връзка чрез GetConnection() и да използвате API на libpg. (Възможно е по-късно да коригирам тази несправедливост).
База данни
#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;
};Всички параметри за свързване с базата данни се вземат от околната среда, така че също е необходимо да създадете и настроите файл .env
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Можете да видите целия код на

И настъпи последният етап на написването на dockerfile и docker-compose.yml. Да кажа честно, това отне голяма част от времето, и не само защото съм начинаещ, който постоянно трябваше да изгражда библиотеките наново, а заради подводните камъни на conan. Например, за да може conan да изтегли, инсталира и компилира необходимите зависимости, не е достатъчно просто да се изпълни "conan install .", но също така трябва да се предаде параметър -s compiler.libcxx=libstdc++11, иначе рискувате да получите куп грешки на етапа на свързване на приложението. Прекарах няколко часа с тази грешка и се надявам, че тази статия ще помогне на другите хора да решат този проблем по-бързо.
След написването на docker-compose.yml, по съвет на моя приятел добавих поддръжка на и сега можете да получите пълен шаблон за REST API услуга на C++, с настроено окружение и стартирана PostgreSQL, просто въвеждайки "cookiecutter " в конзолата. А след това "docker-compose up --build".
Надявам се, че този шаблон ще помогне на новаците в техния труден път на разработване на REST API приложения на великкия и могъщ, но толкова тромав език, какъвто е C++.
Също така, силно препоръчвам да прочетете статия. В нея по-подробно се обяснява как да работите с POCO и да напишете своя REST API услуга.
Източник: habr.com
