I den här artikeln kommer jag att prata om hur jag skapade en mall (cookiecutter) och satte upp en miljö för att skriva en REST API-tjänst i C++ med hjälp av docker/docker-compose och conan package manager.
Under nästa hackathon, där jag deltog som backend-utvecklare, uppstod frågan om vad man skulle använda för att skriva nästa mikrotjänst. Allt som har skrivits hittills är skrivet av mig och min
Så vi stod inför uppgiften att skriva en högbelastningstjänst, vars huvuduppgift var att förbehandla de data som kom till den och skriva den till databasen. Och efter ännu en rökpaus föreslog en vän att jag som C++-utvecklare skulle skriva den här tjänsten med hjälp av proffsen. Att argumentera för detta är att det kommer att bli snabbare, mer produktivt, och i allmänhet kommer juryn att vara nöjd med hur vi vet hur vi ska hantera teamets resurser. Varpå jag svarade att jag aldrig gjort sådana saker i C++ och lätt kunde ägna de återstående 20+ timmarna åt att söka, sammanställa och länka lämpliga bibliotek. Enkelt uttryckt, jag kyckling ut. Det var vad vi bestämde oss för och avslutade lugnt allt i Python.
Nu, under den påtvingade självisoleringen, bestämde jag mig för att ta reda på hur man skriver tjänster i C++. Det första man skulle göra var att välja ett lämpligt bibliotek. Mitt val föll på
conanfile.txt
[kräver]poco/1.9.3
libpq/11.5
och med ett enkelt kommando "conan install ." installera de nödvändiga biblioteken. Naturligtvis var det också nödvändigt att göra ändringar i
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
Efter det började jag leta efter ett bibliotek att arbeta med PostgreSQL, eftersom det var det jag hade liten erfarenhet av att arbeta med, och det var också det som våra Python-tjänster interagerade med. Och vet du vad jag lärde mig? Det är i POCO! Men conan vet inte att det är i POCO och vet inte hur man bygger det; det finns en föråldrad konfigurationsfil i förvaret (jag har redan skrivit om detta fel till skaparna av POCO). Det betyder att du måste leta efter ett annat bibliotek.
Och då föll mitt val på ett mindre populärt bibliotek
Nästa steg var att skriva en tjänstemall som kan behandla förfrågningar.
Vi måste ärva vår TemplateServerApp-klass från Poco::Util::ServerApplication och åsidosätta huvudmetoden.
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;
}
I huvudmetoden måste vi ställa in parametrarna: port, antal trådar och köstorlek. Och viktigast av allt, du måste ange en hanterare för inkommande förfrågningar. Detta görs genom att skapa en fabrik
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
I mitt fall skapar det helt enkelt samma hanterare varje gång - TemplateServerAppHandler. Det är här vi kan placera vår affärslogik.
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();
}
};
Jag skapade också en klassmall för att arbeta med PostgreSQL. För att utföra enkel SQL, som att skapa en tabell, finns det en metod ExecuteSQL(). För mer komplexa frågor eller datahämtning måste du skaffa en anslutning via GetConnection() och använd libpg API. (Kanske senare kommer jag att rätta till denna orättvisa).
Databas
#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;
};
Alla parametrar för att ansluta till databasen är hämtade från miljön, så du måste också skapa och konfigurera .env-filen
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
Du kan se all kod på
Och nu kommer det sista steget av att skriva dockerfilen och docker-compose.yml. Om jag ska vara ärlig tog detta det mesta av tiden, och inte bara för att jag är en noob, för att det var nödvändigt att bygga om biblioteken varje gång, utan på grund av fallgroparna med conan. Till exempel, för att conan ska kunna ladda ner, installera och bygga de nödvändiga beroenden, räcker det inte med att ladda ner "conan install .", det måste också skicka parametern -s compiler.libcxx=libstdc++11, annars du riskerar att få en massa fel vid länkningsstadiet för din ansökan. Jag har fastnat med det här felet i flera timmar och jag hoppas att den här artikeln kommer att hjälpa andra att lösa det här problemet på kortare tid.
Därefter, efter att ha skrivit docker-compose.yml, på inrådan av min vän, lade jag till stöd
Jag hoppas att den här mallen kommer att hjälpa nybörjare på deras svåra väg att utveckla REST API-applikationer i det fantastiska och kraftfulla, men så klumpiga språket som C++.
Dessutom rekommenderar jag starkt att läsa här
Källa: will.com