Microservices σε C++. Μυθοπλασία ή πραγματικότητα;

Microservices σε C++. Μυθοπλασία ή πραγματικότητα;

Σε αυτό το άρθρο θα μιλήσω για το πώς δημιούργησα ένα πρότυπο (cookiecutter) και ρύθμισα ένα περιβάλλον για τη σύνταξη μιας υπηρεσίας REST API σε C++ χρησιμοποιώντας docker/docker-compose και τον διαχειριστή πακέτων conan.

Κατά τη διάρκεια του επόμενου hackathon, στο οποίο συμμετείχα ως προγραμματιστής backend, προέκυψε το ερώτημα τι να χρησιμοποιήσω για να γράψω την επόμενη microservice. Όλα όσα έχουν γραφτεί μέχρι τώρα τα γράφαμε εγώ και τα δικά μου σύντροφος στην Python, αφού ο συνάδελφός μου ήταν ειδικός σε αυτόν τον τομέα και ανέπτυξε επαγγελματικά backends, ενώ εγώ γενικά ήμουν προγραμματιστής ενσωματωμένων συστημάτων και έγραφα στην εξαιρετική και τρομερή C++ και μόλις έμαθα Python στο πανεπιστήμιο.

Έτσι, βρεθήκαμε αντιμέτωποι με το καθήκον να γράψουμε μια υπηρεσία υψηλού φορτίου, το κύριο καθήκον της οποίας ήταν η προεπεξεργασία των δεδομένων που έρχονται σε αυτήν και η εγγραφή τους στη βάση δεδομένων. Και μετά από άλλο ένα διάλειμμα καπνού, ένας φίλος μου πρότεινε, ως προγραμματιστής C++, να γράψω αυτήν την υπηρεσία χρησιμοποιώντας τους επαγγελματίες. Το επιχείρημα αυτό είναι ότι θα είναι πιο γρήγορο, πιο παραγωγικό και γενικά, η κριτική επιτροπή θα είναι ευχαριστημένη με το πώς ξέρουμε πώς να διαχειριζόμαστε τους πόρους της ομάδας. Στο οποίο απάντησα ότι δεν είχα κάνει ποτέ τέτοια πράγματα στη C++ και μπορούσα εύκολα να αφιερώσω τις υπόλοιπες 20+ ώρες στην αναζήτηση, τη μεταγλώττιση και τη σύνδεση κατάλληλων βιβλιοθηκών. Με απλά λόγια, ξετρελάθηκα. Αυτό αποφασίσαμε και ήρεμα ολοκληρώσαμε τα πάντα στην Python.

Τώρα, κατά τη διάρκεια της αναγκαστικής αυτο-απομόνωσης, αποφάσισα να καταλάβω πώς να γράφω υπηρεσίες σε C++. Το πρώτο πράγμα που έπρεπε να κάνετε ήταν να αποφασίσετε για μια κατάλληλη βιβλιοθήκη. Η επιλογή μου έπεσε ΜΙΚΡΕΣ, δεδομένου ότι ήταν γραμμένο σε αντικειμενοστραφή στυλ και διέθετε επίσης κανονική τεκμηρίωση. Επίσης, προέκυψε το ερώτημα σχετικά με την επιλογή ενός συστήματος συναρμολόγησης. Μέχρι αυτό το σημείο έχω δουλέψει μόνο με Visual Studio, IAR και γυμνά makefiles. Και κανένα από αυτά τα συστήματα δεν μου άρεσε, αφού σχεδίαζα να τρέξω ολόκληρη την υπηρεσία σε ένα docker container. Τότε αποφάσισα να προσπαθήσω να ανακαλύψω το cmake και έναν ενδιαφέροντα διαχειριστή πακέτων conan. Αυτός ο διαχειριστής πακέτων σάς επέτρεψε να καταχωρήσετε όλες τις εξαρτήσεις σε ένα αρχείο

conanfile.txt
[απαιτείται]poco/1.9.3
libpq/11.5

[γεννήτριες] cmake

και με μια απλή εντολή "conan install ." εγκαταστήστε τις απαραίτητες βιβλιοθήκες. Φυσικά, ήταν επίσης απαραίτητο να γίνουν αλλαγές σε

CMakeLists.txt

include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})

Μετά από αυτό, άρχισα να ψάχνω για μια βιβλιοθήκη για να δουλέψω με την PostgreSQL, καθώς ήταν αυτή με την οποία είχα μικρή εμπειρία να δουλέψω, και ήταν επίσης αυτή με την οποία αλληλεπιδρούσαν οι υπηρεσίες Python μας. Και ξέρεις τι έμαθα; Είναι στο POCO! Αλλά ο Conan δεν γνωρίζει ότι είναι στο POCO και δεν ξέρει πώς να το δημιουργήσει· υπάρχει ένα παλιό αρχείο ρυθμίσεων στο αποθετήριο (έχω ήδη γράψει για αυτό το σφάλμα στους δημιουργούς του POCO). Αυτό σημαίνει ότι θα πρέπει να αναζητήσετε άλλη βιβλιοθήκη.

Και τότε η επιλογή μου έπεσε σε μια λιγότερο δημοφιλή βιβλιοθήκη libpg. Και ήμουν απίστευτα τυχερός, ήταν ήδη στο conan και μάλιστα συναρμολογούνταν και συναρμολογούνταν.

Το επόμενο βήμα ήταν να γράψετε ένα πρότυπο υπηρεσίας που να μπορεί να επεξεργάζεται αιτήματα.
Πρέπει να κληρονομήσουμε την κλάση 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

Μπορείτε να δείτε όλο τον κώδικα στο github.

Microservices σε C++. Μυθοπλασία ή πραγματικότητα;

Και τώρα έρχεται το τελικό στάδιο της σύνταξης του dockerfile και του docker-compose.yml. Για να είμαι ειλικρινής, αυτό πήρε τον περισσότερο χρόνο, και όχι μόνο επειδή είμαι noob, επειδή ήταν απαραίτητο να ξαναχτίζω τις βιβλιοθήκες κάθε φορά, αλλά λόγω των παγίδων του Conan. Για παράδειγμα, για να κατεβάσει το conan, να εγκαταστήσει και να δημιουργήσει τις απαραίτητες εξαρτήσεις, δεν αρκεί να κατεβάσει το “conan install .”, πρέπει επίσης να περάσει την παράμετρο -s compiler.libcxx=libstdc++11, διαφορετικά κινδυνεύετε να λάβετε ένα σωρό σφάλματα στο στάδιο σύνδεσης της αίτησής σας. Έχω κολλήσει με αυτό το σφάλμα για αρκετές ώρες και ελπίζω ότι αυτό το άρθρο θα βοηθήσει άλλους ανθρώπους να λύσουν αυτό το πρόβλημα σε λιγότερο χρόνο.

Στη συνέχεια, αφού έγραψα το docker-compose.yml, κατόπιν συμβουλής του φίλου μου, πρόσθεσα υποστήριξη μπισκότα και τώρα μπορείτε να αποκτήσετε ένα πλήρες πρότυπο για μια υπηρεσία REST API σε C++, με προσαρμοσμένο περιβάλλον και εγκατεστημένη PostgreSQL, απλά εισάγοντας το "cookiecutter" στην κονσόλα https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Και μετά «docker-compose up —build».

Ελπίζω ότι αυτό το πρότυπο θα βοηθήσει τους αρχάριους στη δύσκολη διαδρομή τους για την ανάπτυξη εφαρμογών REST API σε εξαιρετική και ισχυρή, αλλά μια τόσο αδέξια γλώσσα όπως η C++.
Επίσης, συνιστώ ανεπιφύλακτα να διαβάσετε εδώ αυτό άρθρο. Εξηγεί με περισσότερες λεπτομέρειες πώς να εργαστείτε με το POCO και να γράψετε τη δική σας υπηρεσία REST API.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο