Σε αυτό το άρθρο θα μιλήσω για το πώς δημιούργησα ένα πρότυπο (cookiecutter) και ρύθμισα ένα περιβάλλον για τη σύνταξη μιας υπηρεσίας REST API σε C++ χρησιμοποιώντας docker/docker-compose και τον διαχειριστή πακέτων conan.
Κατά τη διάρκεια του επόμενου hackathon, στο οποίο συμμετείχα ως προγραμματιστής backend, προέκυψε το ερώτημα τι να χρησιμοποιήσω για να γράψω την επόμενη microservice. Όλα όσα έχουν γραφτεί μέχρι τώρα τα γράφαμε εγώ και τα δικά μου
Έτσι, βρεθήκαμε αντιμέτωποι με το καθήκον να γράψουμε μια υπηρεσία υψηλού φορτίου, το κύριο καθήκον της οποίας ήταν η προεπεξεργασία των δεδομένων που έρχονται σε αυτήν και η εγγραφή τους στη βάση δεδομένων. Και μετά από άλλο ένα διάλειμμα καπνού, ένας φίλος μου πρότεινε, ως προγραμματιστής 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 και να παρακάμψουμε την κύρια μέθοδο.
TemplateServer App
#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;
}
Στην κύρια μέθοδο πρέπει να ορίσουμε τις παραμέτρους: port, αριθμός νημάτων και μέγεθος ουράς. Και το πιο σημαντικό, πρέπει να καθορίσετε έναν χειριστή για τα εισερχόμενα αιτήματα. Αυτό γίνεται με τη δημιουργία ενός εργοστασίου
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() και χρησιμοποιήστε το 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. Για να είμαι ειλικρινής, αυτό πήρε τον περισσότερο χρόνο, και όχι μόνο επειδή είμαι noob, επειδή ήταν απαραίτητο να ξαναχτίζω τις βιβλιοθήκες κάθε φορά, αλλά λόγω των παγίδων του Conan. Για παράδειγμα, για να κατεβάσει το conan, να εγκαταστήσει και να δημιουργήσει τις απαραίτητες εξαρτήσεις, δεν αρκεί να κατεβάσει το “conan install .”, πρέπει επίσης να περάσει την παράμετρο -s compiler.libcxx=libstdc++11, διαφορετικά κινδυνεύετε να λάβετε ένα σωρό σφάλματα στο στάδιο σύνδεσης της αίτησής σας. Έχω κολλήσει με αυτό το σφάλμα για αρκετές ώρες και ελπίζω ότι αυτό το άρθρο θα βοηθήσει άλλους ανθρώπους να λύσουν αυτό το πρόβλημα σε λιγότερο χρόνο.
Στη συνέχεια, αφού έγραψα το docker-compose.yml, κατόπιν συμβουλής του φίλου μου, πρόσθεσα υποστήριξη
Ελπίζω ότι αυτό το πρότυπο θα βοηθήσει τους αρχάριους στη δύσκολη διαδρομή τους για την ανάπτυξη εφαρμογών REST API σε εξαιρετική και ισχυρή, αλλά μια τόσο αδέξια γλώσσα όπως η C++.
Επίσης, συνιστώ ανεπιφύλακτα να διαβάσετε εδώ
Πηγή: www.habr.com