В тази статия ще говоря за това как създадох шаблон (cookiecutter) и настроих среда за писане на REST API услуга в C++, използвайки docker/docker-compose и мениджъра на пакети conan.
По време на поредния хакатон, в който участвах като бекенд разработчик, възникна въпросът с какво да напиша следващата микроуслуга. Всичко написано дотук е написано от мен и моите
И така, бяхме изправени пред задачата да напишем услуга с голямо натоварване, чиято основна задача беше да обработим предварително данните, които идват към нея, и да ги запишем в базата данни. И след още една пауза, един приятел ми предложи, като C++ разработчик, да напиша тази услуга, използвайки професионалистите. Аргументът за това е, че ще бъде по-бърз, по-продуктивен и като цяло журито ще бъде възхитено от това как знаем как да управляваме ресурсите на екипа. На което аз отговорих, че никога не съм правил подобни неща в C++ и спокойно мога да посветя останалите 20+ часа на търсене, компилиране и свързване на подходящи библиотеки. Просто казано, изплаших се. Това решихме и спокойно завършихме всичко в Python.
Сега, по време на принудителната самоизолация, реших да разбера как да пиша услуги на C++. Първото нещо, което трябваше да направите, беше да изберете подходяща библиотека. Изборът ми падна на
conanfile.txt
[изисква]poco/1.9.3
libpq/11.5
и с проста команда "conan install ." инсталирайте необходимите библиотеки. Естествено, беше необходимо да се направят и промени в
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
След това започнах да търся библиотека, която да работя с PostgreSQL, тъй като това беше тази, с която имах малко опит в работата, а също и тази, с която нашите Python услуги взаимодействаха. И знаете ли какво научих? В POCO е! Но conan не знае, че е в POCO и не знае как да го изгради; в хранилището има остарял конфигурационен файл (вече писах за тази грешка на създателите на POCO). Това означава, че ще трябва да потърсите друга библиотека.
И тогава изборът ми падна върху по-малко популярна библиотека
Следващата стъпка беше да напиша шаблон за услуга, който може да обработва заявки.
Трябва да наследим нашия клас TemplateServerApp от Poco::Util::ServerApplication и да заменим главния метод.
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;
}
В основния метод трябва да зададем параметрите: порт, брой нишки и размер на опашка. И най-важното, трябва да посочите манипулатор за входящи заявки. Това става чрез създаване на фабрика
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 << "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();
}
};
Също така създадох шаблон за клас за работа с PostgreSQL. За изпълнение на прост SQL, като например създаване на таблица, има метод ExecuteSQL(). За по-сложни заявки или извличане на данни ще трябва да получите връзка чрез GetConnection() и използвайте libpg API. (Може би по-късно ще поправя тази несправедливост).
База данни
#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++.
Също така силно препоръчвам да прочетете тук
Източник: www.habr.com