الخدمات المصغرة في C ++. خيال أم حقيقة؟



في هذه المقالة سأتحدث عن كيفية إنشاء ملف تعريف الارتباط وإعداد البيئة لكتابة خدمة REST API في C ++ باستخدام docker / docker-compose ومدير حزمة conan.


خلال الهاكاثون التالي ، الذي شاركت فيه كمطور خلفية ، نشأ سؤال حول ما أكتب الخدمة الصغيرة التالية. كل ما كتب في هذه اللحظة كتبته أنا وصديقي في Python ، حيث كان زميلي خبيرًا في هذا المجال وكان مشاركًا مهنيًا في تطوير الخلفيات ، بينما كنت بشكل عام مطورًا للأنظمة المدمجة وكتب في لغة C رائعة ورهيبة ++ ، وتعلمت بايثون للتو في الجامعة.


لذلك ، واجهنا مهمة كتابة خدمة محملة للغاية ، كانت مهمتها الرئيسية معالجة البيانات الواردة إليها وكتابتها في قاعدة البيانات. وبعد استراحة دخان أخرى ، اقترح أحد الأصدقاء أنني كمطور C ++ ، أكتب هذه الخدمة على الإيجابيات. يجادل في ذلك بقول أنه سيكون أسرع وأكثر إنتاجية ، وفي الواقع ، ستسعد هيئة المحلفين بكيفية إدارة موارد الفريق. وقد أجبت بأنني لم أقم أبداً بمثل هذه الأشياء في لغة C ++ ، ويمكنني بسهولة تخصيص ما تبقى من 20 ساعة إضافية للعثور على مكتبات مناسبة وتجميعها وربطها. ببساطة ، شعرت بالخوف. هذا ما قرروه وأضفوا كل شيء بهدوء إلى بايثون.


, , ++. , , . POCO, - , . , . Visual Studio, IAR «» makefile. , docker-. cmake conan.

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

[generators]
cmake

ومع الأمر البسيط "تثبيت كونان". تثبيت المكتبات اللازمة. بطبيعة الحال ، كانت التغييرات مطلوبة أيضا في

ملف 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 وتجاوز الطريقة الرئيسية.

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


في الطريقة الرئيسية ، يجب علينا تعيين المعلمات: المنفذ ، وعدد الخيوط وحجم قائمة الانتظار. والأهم من ذلك ، يجب عليك تحديد معالج الطلبات الواردة. يتم ذلك من خلال إنشاء مصنع.

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

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


يمكنك رؤية جميع التعليمات البرمجية على جيثب.



dockerfile docker-compose.yml. , , , , , - conan. , , conan , , «conan install .», -s compiler.libcxx=libstdc++11, . , , .


, docker-compose.yml, cookiecutter REST API ++, c , PostgreSQL, «cookiecutter https://github.com/KovalevVasiliy/cpp_rest_api_template.git». «docker-compose up --build».


, REST API , , ++.
, . POCO REST API .


All Articles