Huduma ndogo katika C++. Fiction au ukweli?

Huduma ndogo katika C++. Fiction au ukweli?

Katika nakala hii nitazungumza juu ya jinsi nilivyounda template (cookiecutter) na kuweka mazingira ya kuandika huduma ya REST API katika C ++ kwa kutumia docker/docker-compose na msimamizi wa kifurushi cha conan.

Wakati wa hackathon iliyofuata, ambayo nilishiriki kama msanidi wa nyuma, swali liliibuka juu ya nini cha kutumia kuandika huduma ndogo inayofuata. Kila kitu ambacho kimeandikwa hadi sasa kiliandikwa na mimi na wangu mwenzetu huko Python, kwa kuwa mwenzangu alikuwa mtaalam katika uwanja huu na vifaa vya nyuma vilivyokuzwa kitaaluma, wakati kwa ujumla nilikuwa msanidi wa mifumo iliyoingia na niliandika katika C++ kubwa na ya kutisha, na nimejifunza Python chuo kikuu.

Kwa hiyo, tulikuwa tunakabiliwa na kazi ya kuandika huduma ya juu ya mzigo, kazi kuu ambayo ilikuwa ni kutayarisha data inayokuja kwake na kuiandika kwenye hifadhidata. Na baada ya mapumziko mengine ya moshi, rafiki alipendekeza kwamba mimi, kama msanidi wa C ++, niandike huduma hii kwa kutumia faida. Kujadili hii ni kwamba itakuwa haraka, yenye tija zaidi, na kwa ujumla, jury itafurahishwa na jinsi tunavyojua jinsi ya kusimamia rasilimali za timu. Ambayo nilijibu kwamba sijawahi kufanya mambo kama haya katika C++ na ningeweza kutumia kwa urahisi saa 20+ zilizobaki kutafuta, kukusanya na kuunganisha maktaba zinazofaa. Kwa ufupi, nilicheka. Hiyo ndio tuliamua na tukakamilisha kwa utulivu kila kitu kwenye Python.

Sasa, wakati wa kujitenga kwa kulazimishwa, niliamua kujua jinsi ya kuandika huduma katika C ++. Jambo la kwanza kufanya lilikuwa kuamua juu ya maktaba inayofaa. Chaguo langu lilianguka POCO, kwa kuwa iliandikwa kwa mtindo unaolenga kitu na pia kujivunia nyaraka za kawaida. Pia, swali liliibuka kuhusu kuchagua mfumo wa kusanyiko. Hadi kufikia hatua hii nimefanya kazi tu na Visual Studio, IAR na faili zisizo wazi. Na hakuna mifumo hii iliyonivutia, kwani nilipanga kuendesha huduma nzima kwenye chombo cha docker. Kisha niliamua kujaribu kujua cmake na meneja wa kifurushi cha kuvutia conan. Kidhibiti hiki cha kifurushi kilikuruhusu kusajili vitegemezi vyote katika faili moja

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

[jenereta] cmake

na kwa amri rahisi "conan install ." kufunga maktaba muhimu. Kwa kawaida, ilikuwa ni lazima pia kufanya mabadiliko kwa

CMakeLists.txt

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

Baada ya hapo, nilianza kutafuta maktaba ya kufanya kazi na PostgreSQL, kwani ndiyo ambayo nilikuwa na uzoefu mdogo wa kufanya kazi nayo, na pia ndiyo huduma zetu za Python ziliingiliana nayo. Na unajua nilichojifunza? Iko katika POCO! Lakini conan hajui kuwa iko kwenye POCO na hajui jinsi ya kuijenga; kuna faili ya usanidi iliyopitwa na wakati kwenye hazina (tayari nimeandika juu ya kosa hili kwa waundaji wa POCO). Hii inamaanisha itabidi utafute maktaba nyingine.

Na kisha chaguo langu likaanguka kwenye maktaba isiyojulikana sana libpg. Na nilikuwa na bahati nzuri sana, ilikuwa tayari iko kwenye conan na hata ilikuwa inakusanywa na kukusanyika.

Hatua inayofuata ilikuwa kuandika kiolezo cha huduma ambacho kinaweza kushughulikia maombi.
Ni lazima turithi darasa letu la TemplateServerApp kutoka Poco::Util::ServerApplication na kubatilisha mbinu kuu.

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

Katika njia kuu lazima tuweke vigezo: bandari, idadi ya nyuzi na ukubwa wa foleni. Na muhimu zaidi, lazima ubainishe kidhibiti kwa maombi yanayoingia. Hii inafanywa kwa kuunda kiwanda

TemplateRequestHandlerFactory

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

Kwa upande wangu, inaunda kidhibiti sawa kila wakati - TemplateServerAppHandler. Hapa ndipo tunaweza kuweka mantiki ya biashara yetu.

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

Pia niliunda kiolezo cha darasa kufanya kazi na PostgreSQL. Ili kufanya SQL rahisi, kama vile kuunda meza, kuna njia TekelezaSQL(). Kwa hoja ngumu zaidi au urejeshaji data, itabidi upate muunganisho kupitia GetConnection() na utumie API ya libpg. (Labda baadaye nitarekebisha dhuluma hii).

Database

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

Vigezo vyote vya kuunganisha kwenye hifadhidata vinachukuliwa kutoka kwa mazingira, kwa hivyo unahitaji pia kuunda na kusanidi faili ya .env.

.a

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

Unaweza kuona nambari zote kwenye github.

Huduma ndogo katika C++. Fiction au ukweli?

Na sasa inakuja hatua ya mwisho ya kuandika faili ya docker na docker-compose.yml. Kuwa waaminifu, hii ilichukua muda mwingi, na si tu kwa sababu mimi ni noob, kwa sababu ilikuwa ni lazima kujenga upya maktaba kila wakati, lakini kwa sababu ya mitego ya conan. Kwa mfano, ili conan kupakua, kusakinisha na kujenga vitegemezi vinavyohitajika, haitoshi kupakua "conan install .", inahitaji pia kupitisha parameta -s compiler.libcxx=libstdc++11, vinginevyo. una hatari ya kupata rundo la makosa katika hatua ya kuunganisha programu yako. Nimekuwa nikikwama na hitilafu hii kwa saa kadhaa na natumaini makala hii itasaidia watu wengine kutatua tatizo hili kwa muda mfupi.

Kisha, baada ya kuandika docker-compose.yml, kwa ushauri wa rafiki yangu, niliongeza msaada mkataji na sasa unaweza kujipatia kiolezo kamili cha huduma ya REST API katika C++, iliyo na mazingira maalum, na PostgreSQL imewekwa, kwa kuingiza tu "cookiecutter" kwenye koni. https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Na kisha "docker-compose up -build".

Natumai kiolezo hiki kitasaidia wanaoanza kwenye njia yao ngumu ya kutengeneza programu za REST API katika lugha kuu na yenye nguvu, lakini lugha ngumu kama C++.
Pia, ninapendekeza sana kusoma hapa hii makala. Inafafanua kwa undani zaidi jinsi ya kufanya kazi na POCO na kuandika huduma yako ya REST API.

Chanzo: mapenzi.com

Kuongeza maoni