
V tomto článku vám ukážu, jak jsem vytvořil šablonu (cookiecutter) a nastavil prostředí pro psaní REST API služby v C++ s využitím docker/docker-compose a balíčkovacího správce conan.
Během dalšího hackathonu, kterého jsem se účastnil jako backendový vývojář, se objevila otázka, na čem napsat další mikroslužbu. Vše, co bylo dosud napsáno, jsem napsal já a můj v jazyce Python, protože můj kolega byl specialistou v této oblasti a profesionálně se zabýval vývojem backendů, zatímco já jsem byl vlastně vývojářem pro vestavěné systémy a psal jsem v úžasném a děsivém C++, zatímco Python jsem se našprtal na univerzitě.
Takže před námi stála úloha napsat vysoce zatěžovaný servis, jehož hlavním úkolem bylo předzpracování přicházejících dat a jejich zápis do DB. A po další přestávce mi kamarád navrhl, abych jako C++ vývojář napsal tento servis v plusách. Argumentoval tím, že to bude rychlejší, výkonnější, a že porota bude nadšená tím, jak dokážeme hospodařit s prostředky týmu. Na to jsem odpověděl, že jsem se nikdy takovými věcmi v C++ nezabýval a mohu s lehkostí věnovat zbývající čas 20+ hodin hledání, kompilaci a skládání vhodných knihoven. Zjednodušeně řečeno, bál jsem se. Na tom jsme se dohodli a v klidu dolaďovali všechno v Pythonu.
Nyní, během nucené izolace, jsem se odvážil pochopit, jak psát služby v C++. První, co bylo třeba udělat, bylo vybrat vhodnou knihovnu. Můj výběr padl na , protože byla napsána objektově orientovaným stylem a mohla se pyšnit dobrou dokumentací. Také se objevila otázka výběru systému sestavení. Do tohoto okamžiku jsem pracoval pouze s Visual Studio, IAR a "holými" makefile. A žádný z těchto systémů mě příliš nelákal, protože jsem plánoval spustit celý servis v dockerovém kontejneru. Tak jsem se rozhodl zkusit se seznámit s cmake a zajímavým balíčkovacím správcem . Tento balíčkovací správce umožňoval napsat všechny závislosti v jednom souboru
conanfile.txt
[requires]
poco/1.9.3
libpq/11.5
[generators]
cmake
a pomocí jednoduchého příkazu „conan install .“ nainstalovat potřebné knihovny. Samozřejmě bylo také nutné provést změny v
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries( ${CONAN_LIBS})
Poté jsem začal hledat knihovnu pro práci s PostgreSQL, protože jsem s ní měl určité zkušenosti a také naše služby na Pythonu s ní interagovaly. A víte, co jsem zjistil? Je v POCO! Ale conan neví, že je v POCO a neumí ji sestavit, v repozitáři leží zastaralý konfigurační soubor (o této chybě jsem již napsal tvůrcům POCO). To znamená, že budu muset hledat jinou knihovnu.
A tak jsem si vybral méně populární knihovnu . A měl jsem obrovské štěstí, byla již v conan a dokonce se sestavovala a linkovala.
Dalším krokem bylo napsat šablonu služby, která umí zpracovávat požadavky.
Musíme zdědit naši třídu TemplateServerApp z Poco::Util::ServerApplication a přepsat metodu 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 << "Server started" << endl;
waitForTerminationRequest(); // wait for CTRL-C or kill
cerr << "Shutting down..." << endl;
s.stop();
return Application::EXIT_OK;
}V metodě main musíme nastavit parametry: port, počet vláken a velikost fronty. A co je nejdůležitější, musíme nastavit zpracovatele příchozích požadavků. To se dělá pomocí vytvoření továrny
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};V mém případě vždy vytváří jeden a ten samý zpracovatel — TemplateServerAppHandler. Právě zde můžeme umístit naši obchodní logiku.
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();
}
};Také jsem vytvořil šablonu třídy pro práci s PostgreSQL. Abych provedl jednoduchý SQL dotaz, například vytvoření tabulky, existuje metoda ExecuteSQL(). Pro složitější dotazy nebo získávání dat bude nutné získat připojení přes GetConnection() a použít API libpg. (Možná později tuto nespravedlnost opravím).
Databáze
#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;
};Všechny parametry pro připojení k databázi se berou z prostředí, takže si také musíte vytvořit a nakonfigurovat soubor .env
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432Můžete si prohlédnout celý kód na

A přišel poslední krok psaní dockerfile a docker-compose.yml. Řeknu upřímně, to zabralo většinu času, a nejen proto, že jsem nováček, a bylo potřeba pokaždé znovu sestavit knihovny, ale kvůli problémům s conanem. Například, aby conan stáhl, nainstaloval a zkompiloval potřebné závislosti, nestačí mu spustit "conan install .", musí také předat parametr -s compiler.libcxx=libstdc++11, jinak riskujete, že dostanete spoustu chyb při linkování vaší aplikace. S touto chybou jsem strávil několik hodin a doufám, že tento článek pomůže ostatním vyřešit tento problém rychleji.
Poté, po napsání docker-compose.yml, na doporučení svého kamaráda jsem přidal podporu a nyní si můžete vytvořit plnohodnotnou šablonu pro REST API službu na C++, s nastaveným prostředím a spuštěným PostgreSQL, jednoduše zadáním do konzole „cookiecutter “. A poté „docker-compose up --build“.
Doufám, že tato šablona pomůže začátečníkům na jejich obtížné cestě vývoje REST API aplikací ve skvělém a mocném, ale tak neobratném jazyce, jako je C++.
Dále velmi doporučuji přečíst si tenhle článek. Detailněji vysvětluje, jak pracovat s POCO a napsat svou vlastní REST API službu.
Zdroj: habr.com
