C++ のマイクロサービス。 フィクションですか、それとも現実ですか?

C++ のマイクロサービス。 フィクションですか、それとも現実ですか?

この記事では、テンプレート (cookiecutter) を作成し、docker/docker-compose と conan パッケージ マネージャーを使用して C++ で REST API サービスを記述するための環境をセットアップする方法について説明します。

私はバックエンド開発者として参加した次のハッカソン中に、次のマイクロサービスを作成するために何を使用するかという疑問が生じました。 これまでに書かれたものはすべて私と私によって書かれました コンパニオン なぜなら、私の同僚はこの分野の専門家であり、専門的にバックエンドを開発していたからです。一方、私は一般的に組み込みシステム開発者で、素​​晴らしくひどい C++ で記述しており、大学で Python を学んだばかりでした。

そこで、私たちは高負荷のサービスを作成するというタスクに直面しました。その主なタスクは、サービスに送られるデータを前処理してデータベースに書き込むことでした。 そして、もう一度タバコを吸った後、友人が、C++ 開発者である私に、専門家を使ってこのサービスを作成することを提案しました。 これはより速く、より生産的になると主張しており、一般的に陪審員は私たちがチームのリソースを管理する方法を知っていることに満足するでしょう。 それに対して私は、C++ でそのようなことをしたことがないので、残りの 20 時間以上を適切なライブラリの検索、コンパイル、リンクに簡単に費やすことができると答えました。 簡単に言えば、私は逃げ出しました。 そう決めて、Python ですべてを淡々と完成させました。

さて、強制隔離中に私は C++ でサービスを書く方法を見つけ出すことにしました。 最初に行うことは、適切なライブラリを決定することでした。 私の選択は当たった リトルなぜなら、それはオブジェクト指向スタイルで書かれており、通常のドキュメントも誇っていたからです。 また、議会制の選択についても疑問が生じました。 これまで、私は Visual Studio、IAR、および裸の Makefile のみを使用してきました。 そして、サービス全体を Docker コンテナーで実行する予定だったので、これらのシステムはどれも私には魅力を感じませんでした。 それから、cmake と興味深いパッケージ マネージャーを見つけようとしました。 コナン。 このパッケージ マネージャーを使用すると、すべての依存関係を XNUMX つのファイルに登録できます。

コナンファイル.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。 そして私は信じられないほど幸運でした、それはすでにコナンにあり、組み立てられ組み立てられていました。

次のステップは、リクエストを処理できるサービス テンプレートを作成することでした。
Poco::Util::ServerApplication から TemplateServerApp クラスを継承し、main メソッドをオーバーライドする必要があります。

テンプレートサーバーアプリ

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

main メソッドでは、ポート、スレッド数、キュー サイズのパラメータを設定する必要があります。 そして最も重要なことは、受信リクエストのハンドラーを指定する必要があることです。 これはファクトリーを作成することで行われます

TemplateRequestHandlerFactory

class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
    virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
    {
        return new 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 を実行するには、次のメソッドがあります。 SQLを実行()。 より複雑なクエリまたはデータ取得の場合は、次の方法で接続を取得する必要があります。 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

すべてのコードは次の場所で確認できます。 ギットハブ。

C++ のマイクロサービス。 フィクションですか、それとも現実ですか?

そして、dockerfile と docker-compose.yml を作成する最終段階が始まります。 正直に言うと、これにはほとんどの時間がかかりましたが、私が初心者であるため、毎回ライブラリを再構築する必要があったためだけでなく、コナンの落とし穴のせいでもありました。 たとえば、conan が必要な依存関係をダウンロード、インストール、ビルドするには、「conan install 」をダウンロードするだけでは十分ではなく、 -s COMPILER.libcxx=libstdc++11 パラメーターを渡す必要があります。それ以外の場合は、アプリケーションのリンク段階で大量のエラーが発生する危険性があります。 私は数時間このエラーに悩まされてきましたが、この記事が他の人がこの問題をより短時間で解決するのに役立つことを願っています。

次に、友人のアドバイスに従って docker-compose.yml を作成した後、サポートを追加しました。 クッキーの抜き型 コンソールに「cookiecutter」と入力するだけで、環境がカスタマイズされ、PostgreSQL がインストールされた C++ の REST API サービス用の本格的なテンプレートを入手できるようになりました。 https://github.com/KovalevVasiliy/cpp_rest_api_template.git」 そして、「docker-compose up —build」します。

このテンプレートが、C++ のような素晴らしく強力ではあるが不器用な言語で REST API アプリケーションを開発するという困難な道を歩む初心者に役立つことを願っています。
また、ここを読むことを強くお勧めします この 記事。 POCO を使用して独自の REST API サービスを作成する方法について詳しく説明します。

出所: habr.com

コメントを追加します