Perkhidmatan mikro dalam C++. Fiksyen atau realiti?

Perkhidmatan mikro dalam C++. Fiksyen atau realiti?

Dalam artikel ini saya akan bercakap tentang cara saya mencipta templat (cookiecutter) dan menyediakan persekitaran untuk menulis perkhidmatan API REST dalam C++ menggunakan docker/docker-compose dan pengurus pakej conan.

Semasa hackathon seterusnya, di mana saya mengambil bahagian sebagai pembangun bahagian belakang, persoalan timbul tentang apa yang perlu digunakan untuk menulis perkhidmatan mikro seterusnya. Semua yang telah ditulis selama ini ditulis oleh saya dan saya rakan seperjuangan dalam Python, kerana rakan sekerja saya pakar dalam bidang ini dan backend yang dibangunkan secara profesional, manakala saya secara amnya adalah pembangun sistem terbenam dan menulis dalam C++ yang hebat dan dahsyat, dan saya baru belajar Python di universiti.

Oleh itu, kami berhadapan dengan tugas untuk menulis perkhidmatan beban tinggi, tugas utamanya adalah untuk memproses terlebih dahulu data yang datang kepadanya dan menulisnya ke pangkalan data. Dan selepas berhenti merokok lagi, seorang rakan mencadangkan saya, sebagai pembangun C++, menulis perkhidmatan ini menggunakan pro. Berhujah ini adalah bahawa ia akan menjadi lebih pantas, lebih produktif, dan secara amnya, juri akan gembira dengan cara kami mengetahui cara menguruskan sumber pasukan. Saya menjawab bahawa saya tidak pernah melakukan perkara sedemikian dalam C++ dan boleh menumpukan baki 20+ jam dengan mudah untuk mencari, menyusun dan memautkan perpustakaan yang sesuai. Ringkasnya, saya mengecewakan. Itulah yang kami putuskan dan dengan tenang menyelesaikan segala-galanya dalam Python.

Sekarang, semasa pengasingan diri secara paksa, saya memutuskan untuk memikirkan cara menulis perkhidmatan dalam C++. Perkara pertama yang perlu dilakukan ialah memutuskan perpustakaan yang sesuai. Pilihan saya jatuh pada POCO, kerana ia ditulis dalam gaya berorientasikan objek dan juga mempunyai dokumentasi biasa. Juga, persoalan timbul tentang memilih sistem pemasangan. Sehingga ke tahap ini saya hanya bekerja dengan Visual Studio, IAR dan fail makefiles kosong. Dan tiada sistem ini menarik minat saya, kerana saya merancang untuk menjalankan keseluruhan perkhidmatan dalam bekas docker. Kemudian saya memutuskan untuk mencuba mencari cmake dan pengurus pakej yang menarik conan. Pengurus pakej ini membenarkan anda mendaftarkan semua kebergantungan dalam satu fail

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

[penjana] cmake

dan dengan arahan mudah "conan install ." memasang perpustakaan yang diperlukan. Sememangnya, ia juga perlu untuk membuat perubahan kepada

CMakeLists.txt

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

Selepas itu, saya mula mencari perpustakaan untuk bekerja dengan PostgreSQL, kerana ia adalah perpustakaan yang saya mempunyai sedikit pengalaman bekerja, dan ia juga yang berinteraksi dengan perkhidmatan Python kami. Dan adakah anda tahu apa yang saya pelajari? Ia ada dalam POCO! Tetapi conan tidak tahu bahawa ia berada dalam POCO dan tidak tahu bagaimana untuk membinanya; terdapat fail konfigurasi lapuk dalam repositori (saya telah menulis tentang ralat ini kepada pencipta POCO). Ini bermakna anda perlu mencari perpustakaan lain.

Dan kemudian pilihan saya jatuh pada perpustakaan yang kurang popular libpg. Dan saya amat bernasib baik, ia sudah pun dalam conan malah sedang dipasang dan dipasang.

Langkah seterusnya ialah menulis templat perkhidmatan yang boleh memproses permintaan.
Kami mesti mewarisi kelas TemplateServerApp kami daripada Poco::Util::ServerApplication dan mengatasi kaedah utama.

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

Dalam kaedah utama kita mesti menetapkan parameter: port, bilangan benang dan saiz baris gilir. Dan yang paling penting, anda mesti menentukan pengendali untuk permintaan masuk. Ini dilakukan dengan mewujudkan kilang

TemplateRequestHandlerFactory

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

Dalam kes saya, ia hanya mencipta pengendali yang sama setiap kali - TemplateServerAppHandler. Di sinilah kita boleh meletakkan logik perniagaan kita.

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

Saya juga mencipta templat kelas untuk berfungsi dengan PostgreSQL. Untuk melaksanakan SQL mudah, seperti mencipta jadual, terdapat kaedah ExecuteSQL(). Untuk pertanyaan yang lebih kompleks atau pengambilan data, anda perlu mendapatkan sambungan melalui GetConnection() dan gunakan API libpg. (Mungkin nanti saya akan betulkan ketidakadilan ini).

Pangkalan Data

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

Semua parameter untuk menyambung ke pangkalan data diambil daripada persekitaran, jadi anda juga perlu mencipta dan mengkonfigurasi fail .env

.env

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

Anda boleh melihat semua kod di github.

Perkhidmatan mikro dalam C++. Fiksyen atau realiti?

Dan kini tibalah peringkat akhir untuk menulis fail docker dan docker-compose.yml. Sejujurnya, ini mengambil sebahagian besar masa, dan bukan sahaja kerana saya seorang noob, kerana ia perlu membina semula perpustakaan setiap kali, tetapi kerana perangkap conan. Sebagai contoh, untuk conan memuat turun, memasang dan membina kebergantungan yang diperlukan, ia tidak mencukupi untuk memuat turun β€œconan install .”, ia juga perlu melepasi parameter -s compiler.libcxx=libstdc++11, jika tidak anda berisiko mendapat banyak ralat pada peringkat pemautan aplikasi anda. Saya telah terperangkap dengan ralat ini selama beberapa jam dan saya harap artikel ini akan membantu orang lain menyelesaikan masalah ini dalam masa yang singkat.

Seterusnya, selepas menulis docker-compose.yml, atas nasihat rakan saya, saya menambah sokongan tukang masak dan kini anda boleh mendapatkan sendiri templat lengkap untuk perkhidmatan REST API dalam C++, dengan persekitaran tersuai dan PostgreSQL dipasang, hanya dengan memasukkan "cookiecutter" ke dalam konsol https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Dan kemudian "docker-compose up -build".

Saya berharap templat ini akan membantu pemula dalam laluan sukar mereka untuk membangunkan aplikasi API REST dalam bahasa yang hebat dan berkuasa, tetapi bahasa yang kekok seperti C++.
Juga, saya sangat mengesyorkan membaca di sini ini artikel. Ia menerangkan dengan lebih terperinci cara bekerja dengan POCO dan menulis perkhidmatan API REST anda sendiri.

Sumber: www.habr.com

Tambah komen