Mikropalvelut C++:ssa. Fiktiota vai todellisuutta?

Mikropalvelut C++:ssa. Fiktiota vai todellisuutta?

Tässä artikkelissa puhun siitä, kuinka loin mallin (cookiecutter) ja määritin ympäristön REST API -palvelun kirjoittamista varten C++:ssa käyttämällä docker/docker-composea ja conan-pakettienhallintaa.

Seuraavan hackathonin aikana, johon osallistuin taustakehittäjänä, heräsi kysymys, mitä käyttää seuraavan mikropalvelun kirjoittamiseen. Kaikki, mitä tähän mennessä on kirjoitettu, on minun ja minun kirjoittama ystävä Pythonissa, koska kollegani oli tämän alan asiantuntija ja ammattimaisesti kehittänyt taustaohjelmia, kun taas minä olin yleensä sulautettujen järjestelmien kehittäjä ja kirjoitin mahtavalla ja kauhealla C++:lla, ja opin juuri yliopistossa Pythonin.

Edessämme oli siis suuren kuormituksen palvelun kirjoittaminen, jonka päätehtävänä oli esikäsitellä siihen tuleva data ja kirjoittaa se tietokantaan. Ja toisen savutauon jälkeen ystäväni ehdotti, että C++-kehittäjänä kirjoittaisin tämän palvelun ammattilaisten avulla. Väitetään, että se on nopeampi, tuottavampi, ja yleensä tuomaristo on iloinen siitä, kuinka osaamme hallita joukkueen resursseja. Tähän vastasin, että en ole koskaan tehnyt tällaisia ​​asioita C++:lla ja voisin helposti käyttää loput 20+ tuntia sopivien kirjastojen etsimiseen, kokoamiseen ja linkittämiseen. Yksinkertaisesti sanottuna tyrmäsin. Näin päätimme ja saimme kaiken rauhallisesti loppuun Pythonissa.

Nyt pakotetun itseeristyksen aikana päätin keksiä, miten kirjoitan palvelut C++:lla. Ensimmäinen asia oli valita sopiva kirjasto. Valintani kaatui HYVIN, koska se oli kirjoitettu olio-tyyliin ja siinä oli myös normaali dokumentaatio. Heräsi myös kysymys kokoonpanojärjestelmän valinnasta. Tähän asti olen työskennellyt vain Visual Studion, IAR:n ja paljaiden make-tiedostojen kanssa. Ja mikään näistä järjestelmistä ei miellyttänyt minua, koska suunnittelin ajavani koko palvelun telakointikontissa. Sitten päätin yrittää keksiä cmaken ja mielenkiintoisen paketinhallinnan conan. Tämän paketinhallinnan avulla voit rekisteröidä kaikki riippuvuudet yhteen tiedostoon

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

[generaattorit] cmake

ja yksinkertaisella komennolla "conan install." asentaa tarvittavat kirjastot. Luonnollisesti oli myös tarpeen tehdä muutoksia

CMakeLists.txt

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

Sen jälkeen aloin etsiä kirjastoa työskentelemään PostgreSQL:n kanssa, koska sen kanssa minulla oli vähän kokemusta työskentelystä, ja se oli myös se, jonka kanssa Python-palvelumme olivat vuorovaikutuksessa. Ja tiedätkö mitä opin? Se on POCOssa! Mutta conan ei tiedä, että se on POCO:ssa, eikä osaa rakentaa sitä; arkistossa on vanhentunut asetustiedosto (olen jo kirjoittanut tästä virheestä POCO:n luojille). Tämä tarkoittaa, että sinun on etsittävä toinen kirjasto.

Ja sitten valintani putosi vähemmän suosittuun kirjastoon libpg. Ja minulla oli uskomattoman onnekas, se oli jo valmiina ja sitä jopa koottiin ja koottiin.

Seuraava askel oli kirjoittaa palvelumalli, joka voi käsitellä pyyntöjä.
Meidän on perittävä TemplateServerApp-luokkamme Poco::Util::ServerApplicationilta ja ohitettava päämenetelmä.

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

Päämenetelmässä meidän on asetettava parametrit: portti, säikeiden lukumäärä ja jonon koko. Ja mikä tärkeintä, sinun on määritettävä käsittelijä saapuville pyynnöille. Tämä tehdään luomalla tehdas

TemplateRequestHandlerFactory

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

Minun tapauksessani se yksinkertaisesti luo saman käsittelijän joka kerta - TemplateServerAppHandler. Tähän voimme sijoittaa liiketoimintalogiikkamme.

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

Tein myös luokkamallin työskentelemään PostgreSQL:n kanssa. Yksinkertaisen SQL:n suorittamiseen, kuten taulukon luomiseen, on menetelmä ExecuteSQL(). Monimutkaisempia kyselyitä tai tietojen hakua varten sinun on muodostettava yhteys kautta Hanki yhteys () ja käytä libpg API:ta. (Ehkä myöhemmin korjaan tämän epäoikeudenmukaisuuden).

tietokanta

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

Kaikki tietokantaan yhdistämisen parametrit otetaan ympäristöstä, joten sinun on myös luotava ja määritettävä .env-tiedosto

.env

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

Näet kaikki koodit osoitteessa github.

Mikropalvelut C++:ssa. Fiktiota vai todellisuutta?

Ja nyt tulee docker-tiedoston ja docker-compose.yml:n kirjoittamisen viimeinen vaihe. Ollakseni rehellinen, tämä vei suurimman osan ajasta, eikä vain siksi, että olen noob, koska kirjastot piti rakentaa uudelleen joka kerta, vaan myös conanin ansojen vuoksi. Esimerkiksi, jotta conan voi ladata, asentaa ja rakentaa tarvittavat riippuvuudet, ei riitä, että se lataa "conan install .", sen on myös läpäistävä parametri -s compiler.libcxx=libstdc++11, muuten saatat saada joukon virheitä hakemuksesi linkitysvaiheessa. Olen ollut jumissa tämän virheen kanssa useita tunteja ja toivon, että tämä artikkeli auttaa muita ihmisiä ratkaisemaan tämän ongelman lyhyemmässä ajassa.

Seuraavaksi kirjoitettuani docker-compose.yml, ystäväni neuvosta, lisäsin tukea piparimuotti ja nyt voit hankkia itsellesi täyden mallin REST API -palvelua varten C++:ssa, mukautetulla ympäristöllä ja asennettuna PostgreSQL:llä yksinkertaisesti kirjoittamalla "cookiecutter" konsoliin https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Ja sitten "telakka-säveltää - rakentaa".

Toivon, että tämä malli auttaa aloittelijoita heidän vaikealla tiellään kehittää REST API -sovelluksia mahtavalla ja tehokkaalla, mutta niin kömpelöllä kielellä kuin C++.
Lisäksi suosittelen lukemista täältä tämä artikla. Siinä kerrotaan yksityiskohtaisemmin, kuinka voit työskennellä POCO:n kanssa ja kirjoittaa oman REST API -palvelun.

Lähde: will.com

Lisää kommentti