C++-da mikroservislər. Fantastika yoxsa reallıq?

C++-da mikroservislər. Fantastika yoxsa reallıq?

Bu yazıda mən docker/docker-compose və conan paket menecerindən istifadə edərək şablonu (cookiecutter) necə yaratdığımdan və C++ dilində REST API xidmətinin yazılması üçün mühit qurduğumdan danışacağam.

Backend developer kimi iştirak etdiyim növbəti hackathon zamanı növbəti mikroservisin yazılması üçün nədən istifadə etmək barədə sual yarandı. İndiyə qədər yazılanların hamısını mən və mənim yazdıqlarım yoldaş Python-da, həmkarım bu sahədə mütəxəssis olduğu və peşəkar şəkildə inkişaf etmiş backendlər olduğu üçün, mən isə ümumiyyətlə quraşdırılmış sistemlər tərtibatçısı idim və böyük və dəhşətli C++ dilində yazırdım və Python dilini universitetdə yeni öyrəndim.

Beləliklə, əsas vəzifəsi ona gələn məlumatları əvvəlcədən emal etmək və verilənlər bazasına yazmaq olan yüksək yüklü bir xidmət yazmaq vəzifəsi ilə qarşılaşdıq. Və növbəti tüstü fasiləsindən sonra bir dostum mənə C++ tərtibatçısı kimi bu xidməti peşəkarlardan istifadə edərək yazmağı təklif etdi. Bunun daha sürətli, daha məhsuldar olacağı və ümumiyyətlə, münsiflər heyəti komandanın resurslarını necə idarə edəcəyimizi bildiyimizdən məmnun qalacaq. Mən cavab verdim ki, mən heç vaxt C++ dilində belə şeylər etməmişəm və qalan 20+ saatı rahatlıqla uyğun kitabxanaları axtarmağa, tərtib etməyə və əlaqələndirməyə ayıra bilərəm. Sadə dillə desək, tüstüdən çıxdım. Buna qərar verdik və Python-da hər şeyi sakitcə tamamladıq.

İndi məcburi özünütəcrid zamanı C++-da xidmətlərin necə yazılacağını anlamağa qərar verdim. Ediləcək ilk şey uyğun bir kitabxana seçmək idi. Seçimim üzərinə düşdü POCO, çünki o, obyekt yönümlü üslubda yazılmışdır və həm də normal sənədlərlə öyünür. Həmçinin, montaj sisteminin seçilməsi ilə bağlı sual yarandı. Bu nöqtəyə qədər mən yalnız Visual Studio, IAR və çılpaq makefiles ilə işləmişəm. Və bu sistemlərin heç biri mənə müraciət etmədi, çünki bütün xidməti docker konteynerində idarə etməyi planlaşdırırdım. Sonra cmake və maraqlı bir paket meneceri tapmağa çalışmaq qərarına gəldim konan. Bu paket meneceri sizə bütün asılılıqları bir faylda qeyd etməyə imkan verir

conanfile.txt
[tələb edir]poco/1.9.3
libpq/11.5

[generatorlar] cmake

və sadə bir əmrlə "conan install ." lazımi kitabxanaları quraşdırın. Təbii ki, dəyişikliklər də etmək lazım idi

CMakeLists.txt

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

Bundan sonra PostgreSQL ilə işləmək üçün kitabxana axtarmağa başladım, çünki onunla işləmək təcrübəm az idi və bu, həm də Python xidmətlərimizlə əlaqə saxladığı kitabxana idi. Və bilirsən nə öyrəndim? POCO-da var! Lakin konan onun POCO-da olduğunu bilmir və onu necə quracağını bilmir; depoda köhnəlmiş konfiqurasiya faylı var (bu səhv haqqında POCO yaradıcılarına artıq yazmışam). Bu o deməkdir ki, başqa kitabxana axtarmalı olacaqsınız.

Və sonra seçimim daha az populyar bir kitabxanaya düşdü libpg. Mən inanılmaz dərəcədə şanslı idim, o, artıq konanda idi və hətta yığılıb yığılırdı.

Növbəti addım sorğuları emal edə bilən xidmət şablonu yazmaq idi.
TemplateServerApp sinfimizi Poco::Util::ServerApplication-dan miras almalı və əsas metodu ləğv etməliyik.

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

Əsas metodda parametrləri təyin etməliyik: port, iplərin sayı və növbə ölçüsü. Və ən əsası, daxil olan sorğular üçün bir işləyici təyin etməlisiniz. Bu, fabrik yaratmaqla həyata keçirilir

TemplateRequestHandlerFactory

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

Mənim vəziyyətimdə o, sadəcə olaraq hər dəfə eyni işləyicini yaradır - TemplateServerAppHandler. İş məntiqimizi yerləşdirə biləcəyimiz yer budur.

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 ilə işləmək üçün sinif şablonu da yaratdım. Cədvəl yaratmaq kimi sadə SQL-i yerinə yetirmək üçün bir üsul var ExecuteSQL(). Daha mürəkkəb sorğular və ya məlumatların axtarışı üçün siz vasitəsilə əlaqə əldə etməli olacaqsınız GetConnection() və libpg API istifadə edin. (Bəlkə sonra bu haqsızlığı düzəldərəm).

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

Verilənlər bazasına qoşulmaq üçün bütün parametrlər mühitdən götürülür, ona görə də siz .env faylını yaratmalı və konfiqurasiya etməlisiniz.

.NS

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

Bütün kodları burada görə bilərsiniz github.

C++-da mikroservislər. Fantastika yoxsa reallıq?

İndi dockerfile və docker-compose.yml faylının yazılmasının son mərhələsi gəlir. Düzünü desəm, bu, çox vaxt apardı və təkcə mən naib olduğum üçün yox, hər dəfə kitabxanaları yenidən qurmaq lazım idi, həm də Konanın tələlərinə görə. Məsələn, conan-ın lazımi asılılıqları yükləməsi, quraşdırması və qurması üçün “conan install .” yükləməsi kifayət deyil, o, həmçinin -s compiler.libcxx=libstdc++11 parametrini keçməlidir, əks halda ərizənizin əlaqələndirilməsi mərhələsində bir sıra səhvlərlə üzləşmək riski ilə üzləşirsiniz. Mən bir neçə saatdır ki, bu səhvlə bağlı qalmışam və ümid edirəm ki, bu məqalə digər insanlara bu problemi daha qısa müddətdə həll etməyə kömək edəcək.

Sonra docker-compose.yml yazdıqdan sonra dostumun məsləhəti ilə dəstək əlavə etdim kökə kəsicisi və indi siz sadəcə olaraq konsola “cookiecutter” daxil etməklə fərdiləşdirilmiş mühitə və PostgreSQL quraşdırılmış C++ dilində REST API xidməti üçün tam hüquqlu şablon əldə edə bilərsiniz. https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Və sonra "docker-compose up —build".

Ümid edirəm ki, bu şablon yeni başlayanlara böyük və güclü, lakin C++ kimi yöndəmsiz bir dildə REST API proqramlarını inkişaf etdirməyin çətin yolunda kömək edəcək.
Həm də burada oxumağı çox tövsiyə edirəm bu məqalə. Bu, POCO ilə necə işləməyi və öz REST API xidmətinizi yazmağı daha ətraflı izah edir.

Mənbə: www.habr.com

Добавить комментарий