In diesem Artikel werde ich darüber sprechen, wie ich eine Vorlage (Cookiecutter) erstellt und eine Umgebung zum Schreiben eines REST-API-Dienstes in C++ mithilfe von Docker/Docker-Compose und dem Conan-Paketmanager eingerichtet habe.
Beim nächsten Hackathon, an dem ich als Backend-Entwickler teilnahm, stellte sich die Frage, womit man den nächsten Microservice schreiben sollte. Alles, was bisher geschrieben wurde, wurde von mir und mir geschrieben
Wir standen also vor der Aufgabe, einen Hochlastdienst zu schreiben, dessen Hauptaufgabe darin bestand, die eingehenden Daten vorzuverarbeiten und in die Datenbank zu schreiben. Und nach einer weiteren Rauchpause schlug mir ein Freund als C++-Entwickler vor, diesen Dienst mit den Profis zu schreiben. Dies wird dadurch begründet, dass es schneller und produktiver sein wird und die Jury im Allgemeinen davon begeistert sein wird, wie wir die Ressourcen des Teams verwalten können. Darauf antwortete ich, dass ich solche Dinge noch nie in C++ gemacht hätte und die verbleibenden mehr als 20 Stunden problemlos dem Suchen, Kompilieren und Verknüpfen geeigneter Bibliotheken widmen könnte. Einfach gesagt, ich habe mich vertan. Dafür haben wir uns entschieden und in aller Ruhe alles in Python fertiggestellt.
Jetzt, während der erzwungenen Selbstisolation, beschloss ich, herauszufinden, wie man Dienste in C++ schreibt. Als erstes galt es, sich für eine geeignete Bibliothek zu entscheiden. Meine Wahl fiel auf
conanfile.txt
[erfordert]poco/1.9.3
libpq/11.5
und mit einem einfachen Befehl „conan install“. Installieren Sie die erforderlichen Bibliotheken. Natürlich mussten auch Änderungen vorgenommen werden
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
Danach begann ich, nach einer Bibliothek für die Arbeit mit PostgreSQL zu suchen, da ich mit dieser nur wenig Erfahrung hatte und mit ihr auch unsere Python-Dienste interagierten. Und wissen Sie, was ich gelernt habe? Es ist in POCO! Aber Conan weiß nicht, dass es sich in POCO befindet und weiß nicht, wie man es erstellt; es gibt eine veraltete Konfigurationsdatei im Repository (ich habe den Erstellern von POCO bereits über diesen Fehler geschrieben). Das bedeutet, dass Sie sich nach einer anderen Bibliothek umsehen müssen.
Und dann fiel meine Wahl auf eine weniger beliebte Bibliothek
Der nächste Schritt bestand darin, eine Servicevorlage zu schreiben, die Anfragen verarbeiten kann.
Wir müssen unsere TemplateServerApp-Klasse von Poco::Util::ServerApplication erben und die Hauptmethode überschreiben.
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;
}
In der Hauptmethode müssen wir die Parameter festlegen: Port, Anzahl der Threads und Warteschlangengröße. Und am wichtigsten ist, dass Sie einen Handler für eingehende Anfragen angeben müssen. Dies geschieht durch die Schaffung einer Fabrik
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
In meinem Fall wird einfach jedes Mal derselbe Handler erstellt – TemplateServerAppHandler. Hier können wir unsere Geschäftslogik platzieren.
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();
}
};
Ich habe auch eine Klassenvorlage für die Arbeit mit PostgreSQL erstellt. Um einfaches SQL auszuführen, beispielsweise das Erstellen einer Tabelle, gibt es eine Methode ExecuteSQL(). Für komplexere Abfragen oder Datenabfragen müssen Sie eine Verbindung über herstellen GetConnection() und verwenden Sie die libpg-API. (Vielleicht werde ich diese Ungerechtigkeit später korrigieren).
Datenbase
#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;
};
Alle Parameter für die Verbindung zur Datenbank werden aus der Umgebung übernommen, daher müssen Sie auch die .env-Datei erstellen und konfigurieren
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
Den gesamten Code finden Sie unter
Und jetzt kommt die letzte Phase des Schreibens der Docker-Datei und der Docker-Compose.yml. Um ehrlich zu sein, hat das die meiste Zeit gedauert, und das nicht nur, weil ich ein Neuling bin, weil es jedes Mal notwendig war, die Bibliotheken neu aufzubauen, sondern auch wegen der Fallstricke von Conan. Damit Conan beispielsweise die erforderlichen Abhängigkeiten herunterladen, installieren und erstellen kann, reicht es nicht aus, „conan install“ herunterzuladen, andernfalls muss auch der Parameter -s Compiler.libcxx=libstdc++11 übergeben werden Sie riskieren, in der Verknüpfungsphase Ihrer Bewerbung eine Reihe von Fehlern zu erhalten. Dieser Fehler beschäftigt mich seit mehreren Stunden und ich hoffe, dass dieser Artikel anderen Leuten hilft, dieses Problem in kürzerer Zeit zu lösen.
Nachdem ich docker-compose.yml geschrieben hatte, fügte ich auf Anraten meines Freundes Unterstützung hinzu
Ich hoffe, dass diese Vorlage Anfängern auf ihrem schwierigen Weg der Entwicklung von REST-API-Anwendungen in der großartigen und leistungsstarken, aber so schwerfälligen Sprache wie C++ hilft.
Außerdem empfehle ich dringend, hier zu lesen
Source: habr.com