Microservice trong C++. Tiểu thuyết hay hiện thực?

Microservice trong C++. Tiểu thuyết hay hiện thực?

Trong bài viết này, tôi sẽ nói về cách tôi tạo một mẫu (cookiecutter) và thiết lập môi trường để viết dịch vụ API REST trong C++ bằng cách sử dụng docker/docker-compose và trình quản lý gói conan.

Trong cuộc thi hackathon tiếp theo, nơi tôi tham gia với tư cách là nhà phát triển phụ trợ, câu hỏi đặt ra là nên sử dụng gì để viết microservice tiếp theo. Tất cả những gì được viết cho đến nay đều do tôi và tôi viết. đồng chí bằng Python, vì đồng nghiệp của tôi là chuyên gia trong lĩnh vực này và phát triển phần phụ trợ một cách chuyên nghiệp, trong khi tôi nói chung là một nhà phát triển hệ thống nhúng và viết bằng C++ tuyệt vời và khủng khiếp, còn tôi mới học Python ở trường đại học.

Vì vậy, chúng tôi phải đối mặt với nhiệm vụ viết một dịch vụ tải cao, nhiệm vụ chính của nó là xử lý trước dữ liệu đến nó và ghi nó vào cơ sở dữ liệu. Và sau một lần nghỉ giải lao khác, một người bạn đã gợi ý rằng tôi, với tư cách là một nhà phát triển C++, hãy viết dịch vụ này bằng cách sử dụng các chuyên gia. Lập luận rằng điều này sẽ nhanh hơn, hiệu quả hơn và nói chung, ban giám khảo sẽ rất vui với cách chúng tôi biết cách quản lý tài nguyên của nhóm. Tôi trả lời rằng tôi chưa bao giờ làm những việc như vậy trong C++ và có thể dễ dàng dành hơn 20 giờ còn lại để tìm kiếm, biên soạn và liên kết các thư viện phù hợp. Nói một cách đơn giản, tôi đã hết hồn. Đó là điều chúng tôi đã quyết định và bình tĩnh hoàn thành mọi thứ bằng Python.

Bây giờ, trong thời gian buộc phải tự cách ly, tôi quyết định tìm cách viết dịch vụ bằng C++. Điều đầu tiên cần làm là quyết định một thư viện phù hợp. Sự lựa chọn của tôi rơi vào POCO, vì nó được viết theo phong cách hướng đối tượng và cũng có tài liệu thông thường. Ngoài ra, câu hỏi đặt ra là chọn một hệ thống lắp ráp. Cho đến thời điểm này, tôi chỉ làm việc với Visual Studio, IAR và các tệp tạo tệp trống. Và không có hệ thống nào trong số này hấp dẫn tôi, vì tôi dự định chạy toàn bộ dịch vụ trong một container docker. Sau đó, tôi quyết định thử tìm hiểu cmake và một trình quản lý gói thú vị conan. Trình quản lý gói này cho phép bạn đăng ký tất cả các phụ thuộc trong một tệp

conanfile.txt
[yêu cầu]poco/1.9.3
libpq/11.5

[máy phát điện] cmake

và bằng một lệnh đơn giản "cài đặt conan." cài đặt các thư viện cần thiết. Đương nhiên, cũng cần phải thực hiện những thay đổi đối với

CMakeLists.txt

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

Sau đó, tôi bắt đầu tìm kiếm một thư viện để làm việc với PostgreSQL, vì đó là thư viện mà tôi có ít kinh nghiệm làm việc và nó cũng là thư viện mà các dịch vụ Python của chúng tôi tương tác. Và bạn có biết tôi đã học được gì không? Nó ở POCO! Nhưng conan không biết rằng nó nằm trong POCO và không biết cách xây dựng nó, có một tệp cấu hình lỗi thời trong kho (tôi đã viết về lỗi này cho những người tạo ra POCO). Điều này có nghĩa là bạn sẽ phải tìm một thư viện khác.

Và rồi sự lựa chọn của tôi rơi vào một thư viện ít phổ biến hơn libpg. Và tôi vô cùng may mắn, nó đã có trong Conan và thậm chí còn được lắp ráp và lắp ráp.

Bước tiếp theo là viết một mẫu dịch vụ có thể xử lý các yêu cầu.
Chúng ta phải kế thừa lớp TemplateServerApp từ Poco::Util::ServerApplication và ghi đè phương thức chính.

Ứng dụng máy chủ mẫu

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

Trong phương thức chính chúng ta phải đặt các tham số: cổng, số lượng luồng và kích thước hàng đợi. Và quan trọng nhất, bạn phải chỉ định một trình xử lý cho các yêu cầu gửi đến. Điều này được thực hiện bằng cách tạo ra một nhà máy

MẫuYêu cầuHandlerFactory

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

Trong trường hợp của tôi, nó chỉ đơn giản tạo ra cùng một trình xử lý mọi lúc - FileServerAppHandler. Đây là nơi chúng ta có thể đặt logic kinh doanh của mình.

MẫuServerAppHandler

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

Tôi cũng đã tạo một mẫu lớp để làm việc với PostgreSQL. Để thực hiện SQL đơn giản, chẳng hạn như tạo bảng, có một phương thức Thực thiSQL(). Đối với các truy vấn hoặc truy xuất dữ liệu phức tạp hơn, bạn sẽ phải có kết nối qua Nhận kết nối() và sử dụng API libpg. (Có lẽ sau này tôi sẽ sửa chữa sự bất công này).

Cơ sở dữ liệu

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

Tất cả các tham số kết nối tới cơ sở dữ liệu đều được lấy từ môi trường nên bạn cũng cần tạo và cấu hình file .env

.NS

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

Bạn có thể xem tất cả mã tại github.

Microservice trong C++. Tiểu thuyết hay hiện thực?

Và bây giờ đến giai đoạn cuối cùng của việc viết dockerfile và docker-compose.yml. Thành thật mà nói, việc này tốn phần lớn thời gian, không chỉ vì tôi là một người mới, vì tôi cần phải xây dựng lại thư viện mỗi lần, mà còn vì những cạm bẫy của conan. Ví dụ: để conan tải xuống, cài đặt và xây dựng các phụ thuộc cần thiết, việc tải xuống “conan install .” là chưa đủ, nó còn cần phải truyền tham số -s trình biên dịch.libcxx=libstdc++11, nếu không thì bạn có nguy cơ gặp phải một loạt lỗi ở giai đoạn liên kết ứng dụng của mình. Tôi đã mắc lỗi này trong vài giờ và tôi hy vọng bài viết này sẽ giúp những người khác giải quyết vấn đề này trong thời gian ngắn hơn.

Tiếp theo, sau khi viết docker-compose.yml, theo lời khuyên của bạn tôi, tôi đã thêm hỗ trợ cắt cookie và bây giờ bạn có thể có cho mình một mẫu chính thức cho dịch vụ API REST trong C++, với môi trường tùy chỉnh và cài đặt PostgreSQL, chỉ bằng cách nhập “cookiecutter” vào bảng điều khiển https://github.com/KovalevVasiliy/cpp_rest_api_template.git" Và sau đó “docker-compose up —build”.

Tôi hy vọng mẫu này sẽ giúp ích cho những người mới bắt đầu trên con đường khó khăn trong việc phát triển các ứng dụng API REST bằng một ngôn ngữ tuyệt vời và mạnh mẽ nhưng vụng về như C++.
Ngoài ra, tôi thực sự khuyên bạn nên đọc ở đây điều này bài báo. Nó giải thích chi tiết hơn cách làm việc với POCO và viết dịch vụ API REST của riêng bạn.

Nguồn: www.habr.com

Thêm một lời nhận xét