Mikroservis u C++. Fikcija ili stvarnost?

Mikroservis u C++. Fikcija ili stvarnost?

U ovom članku ću govoriti o tome kako sam kreirao šablon (cookiecutter) i podesio okruženje za pisanje REST API servisa u C++ koristeći docker/docker-compose i conan menadžer paketa.

Tokom sljedećeg hackathona, u kojem sam učestvovao kao backend developer, postavilo se pitanje šta koristiti za pisanje sljedećeg mikroservisa. Sve što je do sada napisano napisali smo ja i moji druže u Pythonu, pošto je moj kolega bio stručnjak u ovoj oblasti i profesionalno razvijao backendove, dok sam ja generalno bio programer ugrađenih sistema i pisao u sjajnom i strašnom C++, a Python sam naučio tek na fakultetu.

Dakle, bili smo suočeni sa zadatkom da napišemo servis sa velikim opterećenjem, čiji je glavni zadatak bio da predobradi podatke koji mu dolaze i upiše ih u bazu podataka. I nakon još jedne pauze za dim, prijatelj mi je predložio da ja, kao C++ programer, napišem ovu uslugu koristeći profesionalce. Tvrdi se da će to biti brže, produktivnije i općenito, žiri će biti oduševljen kako znamo upravljati resursima tima. Na šta sam odgovorio da nikada nisam radio takve stvari u C++-u i da bih lako mogao posvetiti preostalih 20+ sati traženju, kompajliranju i povezivanju odgovarajućih biblioteka. Jednostavnije rečeno, propao sam. Na to smo se odlučili i mirno završili sve u Pythonu.

Sada, tokom prisilne samoizolacije, odlučio sam da smislim kako da napišem servise na C++. Prvo što je trebalo učiniti je odlučiti se za odgovarajuću biblioteku. Moj izbor je pao POCO, budući da je napisan u objektno orijentisanom stilu i takođe se može pohvaliti normalnom dokumentacijom. Također, postavilo se pitanje izbora montažnog sistema. Do ove tačke sam radio samo sa Visual Studio-om, IAR-om i golim make-fajlovima. I nijedan od ovih sistema mi se nije dopao, pošto sam planirao da pokrenem čitav servis u docker kontejneru. Onda sam odlučio da pokušam da pronađem cmake i zanimljiv menadžer paketa Conan. Ovaj menadžer paketa vam je omogućio da registrujete sve zavisnosti u jednom fajlu

conanfile.txt
[zahtijeva]poco/1.9.3
libpq/11.5

[generatori] cmake

i jednostavnom komandom "conan install ." instalirati potrebne biblioteke. Naravno, bilo je potrebno i izvršiti promjene u

CMakeLists.txt

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

Nakon toga sam počeo da tražim biblioteku za rad sa PostgreSQL-om, budući da je to bila ona sa kojom sam imao malo iskustva u radu, a takođe je bila i ona sa kojom su komunicirali naši Python servisi. I znate li šta sam naučio? U POCO je! Ali conan ne zna da je u POCO-u i ne zna kako da ga napravi; u spremištu postoji zastarjela konfiguracijska datoteka (već sam pisao o ovoj grešci kreatorima POCO-a). To znači da ćete morati potražiti drugu biblioteku.

A onda je moj izbor pao na manje popularnu biblioteku libpg. I imao sam nevjerovatnu sreću, već je bio u Conanu i čak se sklapao i sklapao.

Sljedeći korak je bio pisanje predloška usluge koji može obraditi zahtjeve.
Moramo naslijediti našu TemplateServerApp klasu od Poco::Util::ServerApplication i nadjačati glavni metod.

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;
}

U glavnoj metodi moramo postaviti parametre: port, broj niti i veličinu reda. I što je najvažnije, morate navesti rukovatelja za dolazne zahtjeve. To se radi stvaranjem fabrike

TemplateRequestHandlerFactory

class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
    virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
    {
        return new TemplateServerAppHandler;
    }
};

U mom slučaju, jednostavno svaki put kreira isti rukovalac - TemplateServerAppHandler. Tu možemo postaviti našu poslovnu 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();
    }
};

Napravio sam i predložak klase za rad sa PostgreSQL-om. Za izvođenje jednostavnog SQL-a, kao što je kreiranje tablice, postoji metoda ExecuteSQL(). Za složenije upite ili pronalaženje podataka, morat ćete uspostaviti vezu putem GetConnection() i koristite libpg API. (Možda ću kasnije ispraviti ovu nepravdu).

baza podataka

#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;
};

Svi parametri za povezivanje sa bazom podataka preuzeti su iz okruženja, tako da je potrebno kreirati i konfigurisati .env fajl

.NS

DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432

Sve kodove možete vidjeti na github.

Mikroservis u C++. Fikcija ili stvarnost?

I sada dolazi zadnja faza pisanja dockerfile-a i docker-compose.yml. Da budem iskren, to mi je oduzimalo većinu vremena, i to ne samo zato što sam noob, jer je svaki put bilo potrebno obnavljati biblioteke, već i zbog zamki Conana. Na primjer, da bi conan preuzeo, instalirao i izgradio potrebne ovisnosti, nije dovoljno da preuzme "conan install .", već mora proći i parametar -s compiler.libcxx=libstdc++11, inače rizikujete da dobijete gomilu grešaka u fazi povezivanja vaše aplikacije. Zaglavio sam s ovom greškom nekoliko sati i nadam se da će ovaj članak pomoći drugim ljudima da riješe ovaj problem za kraće vrijeme.

Zatim, nakon što sam napisao docker-compose.yml, po savjetu mog prijatelja, dodao sam podršku kolačić i sada možete sebi nabaviti punopravni predložak za REST API uslugu u C++, sa prilagođenim okruženjem i instaliranim PostgreSQL-om, jednostavnim unošenjem “cookiecutter” u konzolu https://github.com/KovalevVasiliy/cpp_rest_api_template.git" A onda “docker-compose up —build”.

Nadam se da će ovaj šablon pomoći početnicima na njihovom teškom putu razvoja REST API aplikacija na sjajnom i moćnom, ali tako nezgrapnom jeziku kao što je C++.
Također, toplo preporučujem čitanje ovdje ovo članak. Objašnjava detaljnije kako raditi s POCO-om i pisati vlastiti REST API servis.

izvor: www.habr.com

Dodajte komentar