
В тази статия ще разкажа как създадох шаблон (cookiecutter) и настроих среда за разработка на REST API услуга на C++ с използване на docker/docker-compose и пакетния мениджър conan.
По време на поредния хакатон, в който участвах като бекенд разработчик, възникна въпросът на какъв език да напишем следния микросервис. Всичко, което беше написано до момента, бе написано от мен и моя на Python, тъй като моят колега беше специалист в тази област и професионално се занимаваше с разработка на бекендове, докато аз всъщност бях разработчик за вградени системи и пишех на великия и ужасен C++, а Python просто научих в университета.
И така, пред нас стоеше задачата да напишем високонагружен сервис, основната задача на който беше препроцесирането на получаващите се данни и записването им в базата данни. И след поредната почивка, приятелят ми предложи, като разработчик на C++, да напиша този сервис на 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
