Layanan mikro di C++. Fiksi atau kenyataan?

Layanan mikro di C++. Fiksi atau kenyataan?

Pada artikel ini saya akan berbicara tentang bagaimana saya membuat template (cookiecutter) dan mengatur lingkungan untuk menulis layanan REST API di C++ menggunakan docker/docker-compose dan manajer paket conan.

Selama hackathon berikutnya, di mana saya berpartisipasi sebagai pengembang backend, muncul pertanyaan tentang apa yang akan digunakan untuk menulis layanan mikro berikutnya. Segala sesuatu yang telah ditulis sejauh ini ditulis oleh saya dan saya kawan dengan Python, karena kolega saya adalah ahli di bidang ini dan mengembangkan backend secara profesional, sementara saya pada umumnya adalah pengembang sistem tertanam dan menulis dalam C++ yang hebat dan buruk, dan saya baru belajar Python di universitas.

Jadi, kami dihadapkan pada tugas menulis layanan dengan beban tinggi, tugas utamanya adalah memproses terlebih dahulu data yang masuk dan menulisnya ke database. Dan setelah berhenti sejenak, seorang teman menyarankan agar saya, sebagai pengembang C++, menulis layanan ini menggunakan yang profesional. Dengan alasan bahwa ini akan lebih cepat, lebih produktif, dan secara umum, juri akan senang dengan cara kami mengelola sumber daya tim. Saya menjawab bahwa saya belum pernah melakukan hal seperti itu di C++ dan dapat dengan mudah mencurahkan 20+ jam tersisa untuk mencari, menyusun, dan menghubungkan perpustakaan yang sesuai. Sederhananya, saya ketakutan. Itulah yang kami putuskan dan dengan tenang menyelesaikan semuanya dengan Python.

Sekarang, selama isolasi mandiri paksa, saya memutuskan untuk mencari cara menulis layanan dalam C++. Hal pertama yang harus dilakukan adalah memutuskan perpustakaan yang cocok. Pilihanku jatuh KECIL, karena ditulis dalam gaya berorientasi objek dan juga memiliki dokumentasi normal. Juga, muncul pertanyaan tentang memilih sistem perakitan. Sampai saat ini saya hanya bekerja dengan Visual Studio, IAR dan bare makefiles. Dan tidak satu pun dari sistem ini yang menarik bagi saya, karena saya berencana menjalankan seluruh layanan dalam wadah buruh pelabuhan. Kemudian saya memutuskan untuk mencoba mencari tahu cmake dan pengelola paket yang menarik conan. Manajer paket ini memungkinkan Anda mendaftarkan semua dependensi dalam satu file

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

[generator] buat

dan dengan perintah sederhana "conan install." menginstal perpustakaan yang diperlukan. Tentu saja, perubahan juga perlu dilakukan

CMakeLists.txt

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

Setelah itu, saya mulai mencari perpustakaan untuk bekerja dengan PostgreSQL, karena perpustakaan itulah yang saya punya sedikit pengalaman bekerja dengannya, dan juga perpustakaan yang berinteraksi dengan layanan Python kami. Dan tahukah Anda apa yang saya pelajari? Ada di POCO! Tetapi conan tidak tahu bahwa itu ada di POCO dan tidak tahu cara membuatnya; ada file konfigurasi yang sudah ketinggalan zaman di repositori (saya sudah menulis tentang kesalahan ini kepada pembuat POCO). Ini berarti Anda harus mencari perpustakaan lain.

Dan kemudian pilihan saya jatuh pada perpustakaan yang kurang populer libpg. Dan saya sangat beruntung, itu sudah ada di conan dan bahkan sedang dirakit dan dirakit.

Langkah selanjutnya adalah menulis templat layanan yang dapat memproses permintaan.
Kita harus mewarisi kelas TemplateServerApp dari Poco::Util::ServerApplication dan mengganti metode utama.

TemplatServerApp

#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 metode utama kita harus mengatur parameter: port, jumlah thread dan ukuran antrian. Dan yang terpenting, Anda harus menentukan handler untuk permintaan masuk. Hal ini dilakukan dengan mendirikan pabrik

TemplatRequestHandlerFactory

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

Dalam kasus saya, ini hanya membuat handler yang sama setiap saat - TemplateServerAppHandler. Di sinilah kita bisa menempatkan logika bisnis kita.

TemplatServerAppHandler

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 membuat templat kelas untuk bekerja dengan PostgreSQL. Untuk melakukan SQL sederhana, seperti membuat tabel, ada metodenya JalankanSQL(). Untuk pertanyaan atau pengambilan data yang lebih kompleks, Anda harus mendapatkan koneksi melalui Dapatkan Koneksi() dan gunakan API libpg. (Mungkin nanti saya akan memperbaiki ketidakadilan ini).

Basis 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 database diambil dari lingkungan, jadi Anda juga perlu membuat dan mengkonfigurasi file .env

.env

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

Anda dapat melihat semua kode di github.

Layanan mikro di C++. Fiksi atau kenyataan?

Dan kini sampai pada tahap akhir penulisan dockerfile dan docker-compose.yml. Sejujurnya, ini memakan banyak waktu, dan bukan hanya karena saya seorang pemula, karena perpustakaan harus dibangun kembali setiap saat, tetapi karena jebakan conan. Misalnya, agar conan dapat mengunduh, menginstal, dan membangun dependensi yang diperlukan, tidak cukup hanya mengunduh “conan install.”, ia juga harus meneruskan parameter -s compiler.libcxx=libstdc++11, jika tidak Anda berisiko mendapatkan banyak kesalahan pada tahap penautan aplikasi Anda. Saya terjebak dengan kesalahan ini selama beberapa jam dan saya harap artikel ini akan membantu orang lain memecahkan masalah ini dalam waktu yang lebih singkat.

Selanjutnya setelah menulis docker-compose.yml, atas saran teman saya, saya menambahkan dukungan juru masak dan sekarang Anda bisa mendapatkan template lengkap untuk layanan REST API di C++, dengan lingkungan yang disesuaikan, dan PostgreSQL terinstal, cukup dengan memasukkan "cookiecutter" ke dalam konsol https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Dan kemudian "penyusun buruh pelabuhan —membangun".

Saya harap templat ini akan membantu pemula yang kesulitan mengembangkan aplikasi REST API dalam bahasa yang hebat dan kuat, namun canggung seperti C++.
Juga, saya sangat merekomendasikan membaca di sini ini artikel. Ini menjelaskan lebih detail cara bekerja dengan POCO dan menulis layanan REST API Anda sendiri.

Sumber: www.habr.com

Tambah komentar