Микроуслуги во C++. Фикција или реалност?

Микроуслуги во C++. Фикција или реалност?

Во оваа статија ќе зборувам за тоа како создадов шаблон (cookiecutter) и поставив средина за пишување REST API услуга во C++ користејќи docker/docker-compose и менаџерот на пакети conan.

За време на следниот хакатон, на кој учествував како развивач на заднина, се појави прашањето што да се користи за да се напише следниот микросервис. Сè што е напишано досега го напишавме јас и моите другар во Python, бидејќи мојот колега беше експерт во оваа област и професионално развиваше бекендови, додека јас генерално бев развивач на вградени системи и пишував во одличниот и страшен C++, а јас само што научив Python на универзитет.

Значи, се соочивме со задача да напишеме услуга со големо оптоварување, чија главна задача беше да ги преобработи податоците што доаѓаат до неа и да ги запише во базата на податоци. И по уште една пауза за чад, еден пријател ми предложи јас, како развивач на C++, да ја напишам оваа услуга користејќи ги добрите. Тврдејќи го ова е дека ќе биде побрзо, попродуктивно и генерално, жирито ќе биде воодушевено од тоа како знаеме да управуваме со ресурсите на тимот. На што одговорив дека никогаш не сум правел такви работи во C++ и лесно можам да ги посветам преостанатите 20+ часа на пребарување, составување и поврзување соодветни библиотеки. Едноставно кажано, јас изневерена. Тоа е она за што решивме и смирено завршивме сè во Пајтон.

Сега, за време на присилната самоизолација, решив да сфатам како да пишувам услуги во C++. Првото нешто што требаше да се направи беше да се одлучи за соодветна библиотека. Мојот избор падна POCO, бидејќи беше напишано во објектно-ориентиран стил и исто така можеше да се пофали со нормална документација. Исто така, се појави прашањето за избор на систем за склопување. До овој момент работев само со Visual Studio, IAR и голи мејкфајлови. И ниту еден од овие системи не ми се допадна, бидејќи планирав да ја водам целата услуга во докер контејнер. Тогаш решив да се обидам да дознаам cmake и интересен менаџер на пакети конан. Овој менаџер на пакети ви овозможи да ги регистрирате сите зависности во една датотека

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! Но, Конан не знае дека е во POCO и не знае како да го изгради; има застарена конфигурациска датотека во складиштето (веќе им напишав за оваа грешка на креаторите на POCO). Ова значи дека ќе треба да барате друга библиотека.

И тогаш мојот избор падна на помалку популарна библиотека libpg. И имав неверојатна среќа, веќе беше во Конан и дури се склопуваше и склопуваше.

Следниот чекор беше да се напише образец за услуга што може да обработува барања.
Мора да ја наследиме нашата класа 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;
}

Во главниот метод мораме да ги поставиме параметрите: порта, број на нишки и големина на редот. И што е најважно, мора да наведете управувач за дојдовните барања. Ова се прави со создавање фабрика

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 () и користете го libpg API. (Можеби подоцна ќе ја исправам оваа неправда).

База на податоци

#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

.Н.С

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

Можете да го видите целиот код на github.

Микроуслуги во C++. Фикција или реалност?

И сега доаѓа последната фаза на пишување на dockerfile и docker-compose.yml. Да бидам искрен, ова ми одземаше поголемиот дел од времето, и не само затоа што сум глупак, затоа што беше неопходно да се обновуваат библиотеките секој пат, туку и поради замките на Конан. На пример, за да може 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" А потоа „докер-компонирај — изгради“.

Се надевам дека овој шаблон ќе им помогне на почетниците на нивниот тежок пат за развој на REST API апликации на одличниот и моќен, но таков несмасен јазик како C++.
Исто така, топло препорачувам да прочитате овде ова статија. Подетално објаснува како да работите со POCO и да напишете сопствена услуга REST API.

Извор: www.habr.com

Додадете коментар