このオープンソースフレームワークを使用してC ++を学習したばかりのXiaobai用のRPCサービスを構築するのにどのくらい時間がかかりますか?

この記事はC ++ファンデーションの友達に適しています

著者:HelloGitHub-アンソニー

HelloGitHubが立ち上げた「ExplainOpenSource Projects」シリーズでは、この号でC ++ベースのRPCオープンソースフレームワーク-rest_rpcを紹介します。これは、XiaobaiがRPCサービスを迅速に(10分)開発できるようにするフレームワークです。

プロジェクトアドレス:https://github.com/qicosmos/rest_rpc

rest_rpcは、高性能で使いやすいクロスプラットフォームのヘッダーのみのC ++ 11 RPCライブラリです。その目標は、TCP通信を非常にシンプルで使いやすくすることです。ネットワーク通信を理解していない人でもできます。直接かつ迅速に使用してください。始めましょう。同時に、ユーザーは自分のビジネスロジックに注意を払うだけで済みます。

つまり、rest_rpcを使用すると、ネットワークプログラミング関連の知識がなくても、数行のコードで独自のネットワークプログラムすばやく作成でき、非常に便利です。ネットワークプログラミングとRPCフレームワークを開始するのに最適です。

1.予備知識

1.1RPCとは

RPCは、Remote ProcedureCallの略語です。

1.2RPCの用途は何ですか

たとえば、2つのサーバーAとBがあります。AのプログラムがBのアプリケーションによって提供される関数/メソッドをリモートで呼び出したいので、ネットワークを介して必要なメッセージを送信する必要があります。

ただし、メッセージのネットワーク送信には、次のような多くのことが含まれます。

  • クライアントとサーバー間のTCP接続の確立、保守、および切断

  • メッセージのシリアル化とグループ化

  • メッセージのネットワーク送信

  • メッセージの逆シリアル化

  • などなど

RPCの役割は、ネットワーク関連の操作を保護することです。これにより、メモリスペースやマシンにさえ存在しないプログラムを、通常の関数のように呼び出すことができます。

1.3rest_rpcの利点

rest_rpcには多くの利点があります。

  • 使いやすい

  • サブスクリプションモードをサポート

  • 許可futurecallback2種類の非同期呼び出しインターフェイス、関心のあるさまざまな人々に会う

2.クイックスタート

rest_rpcBoost使用前の依存関係は適切にインストールする必要がありますBoost

2.1インストール

git cloneコマンドローカルプロジェクトをダウンロードします:

git clone https://github.com/qicosmos/rest_rpc

2.2ディレクトリ構造

rest_rpcプロジェクトのルートディレクトリにあるファイルとその意味を次の表に示します。

ファイル名 効果
doc rest_rpcパフォーマンステストレポート
クライアントとサーバーの部分を含むrest_rpcの例
含める rest_rpcフレームヘッダーファイル
第三 メッセージのシリアル化と逆シリアル化に使用されるmsgpackサポートライブラリ

2.3ルーチンを実行する

Visual Studioプロジェクトのrest_rpcルーチン、クライアントおよびサーバールーチンは、Visual Studioに格納されexamples/clientexamples/server直接使用されるbasic_client.vcxprojbasic_server.vcxproj、次のように直接公式ルーチンの操作結果にコンパイルされます。

:プロジェクトにはBoost/asioサポートBoostが必要です。インストールされていない場合は、適切にインストールBoostする必要がありBoost、プロジェクトに追加されます。

プロジェクトに追加するBoost  方法次のとおりです。

  1. プロジェクトを開いた後项目メニューバーの→をクリックします属性(ショートカットキー  Alt+ F7

  2. 左を選択しVC++ 目录、右側に、オプションを包含目录库目录に追加し、保存Boost根目录依赖库

私はBoost 1.75ディレクトリインストールするために使用しましたD:\devPack\boost_1_75_0、示されているように構成:

3つの詳細なチュートリアル

3.1前に書く

かどうか服务端または客户端のみに含まれinclude/rest_rpc.hpp、このファイル。

すべてのサンプルコードは、フレームワークとして以下を使用します

#include <iostream>
#include <rest_rpc.hpp>
#include <chrono>
using namespace rest_rpc;
using namespace rest_rpc::rpc_service;

int main(){
    // do something
}

3.2サーバーの作成

サービスを提供できるクライアントを生成するには、いくつかのプロセスがあります。

  1. rpc_server オブジェクトのインスタンス化、リスニングポートの設定およびその他のプロパティ

  2. サーバーが提供するサービスを定義するサービス機能の登録

  3. サービス開始

1)rpc_server

rpc_serverrest_rpcサーバーオブジェクトの場合、rest_rpc::rpc_service名前空間にあるサーバーのサービス、パブリッシュ/サブスクライブ、スレッドプール管理、およびその他の基本機能の登録を担当します。

rpc_serverオブジェクトのインスタンス化を使用して、リスニングポート、スレッドプールサイズを提供する必要があります。次に例を示します。

rpc_server server(9000, 6); // 监听 9000 端口,线程池大小为 6

2)サーバーの登録と起動

rpc_serverregister_handlerサービスを登録する方法とrunサーバーを起動する方法を提供します。具体的な例は次のとおりです。

/*服务函数第一个参数必须为 rpc_conn,然后才是实现功能需要的参数(为可变参数,数量可变,也可以没有*/
std::string hello(rpc_conn conn, std::string name){ 
 /*可以为 void 返回类型,代表调用后不给远程客户端返回消息*/
    return ("Hello " + name); /*返回给远程客户端的内容*/
}


int main(){
    rpc_server server(9000, 6);
    
    /*func_greet 为服务名,远程调用通过服务名确定调用函数*/
    /*hello 为函数,绑定当前服务调用哪个函数*/
    server.register_handler("func_greet", hello);
 
    server.run();//启动服务端
    
    return EXIT_SUCCESS;
}

これはfunctionよい仿函数lambdaの例は以下のとおりです。

ファンクターを使用する

/*仿函数方法*/

struct test_func{
    std::string hello(rpc_conn conn){
        return "Hello Github!";
    }
};


int main(){
    test_func greeting;
    rpc_server server(9000, 6);
    
    /*greet 为服务名,远程调用通过服务名确定调用函数*/
    /*test_func::hello 为函数,绑定当前服务调用哪个函数*/
    /*greeting 为实例化仿函数对象*/
    server.register_handler("greet", &test_func::hello, &greeting);
    
    server.run();//启动服务端
    
    return EXIT_SUCCESS;
}

ラムダメソッドの使用例

/*使用 lambda 方法*/

int main(){
    rpc_server server(9000, 6);
    
    /*call_lambda 为服务名,远程调用通过服务名确定调用函数*/
    /*[&server](rpc_conn conn){...} 为 lambda 对象*/
    server.register_handler("call_lambda", 
                            /*除 conn 外其他参数为可变参数*/
                            [&server](rpc_conn conn /*其他参数可有可无*/) {
                                std::cout << "Hello Github!" << std::endl;
                                // 返回值可有可无
                            });
    
    server.run();//启动服务端
    
    return EXIT_SUCCESS;
}

3)非同期サービスを登録する

さまざまな理由で、リモート呼び出しの戻りを同期できない、または同期したくない場合があります(スレッドの戻りを待つ必要があるなど)。今回はregister_handlerAsyncテンプレートパラメーター(rest_rpc名前空間に配置メソッド化するためだけです。

/*异步服务返回类型为 void*/
void async_greet(rpc_conn conn, const std::string& name) {
    auto req_id = conn.lock()->request_id();// 异步服务需要先保存请求 id

    // 这里新建了一个线程,代表异步处理了一些任务
    std::thread thd([conn, req_id, name] {
        
        std::string ret = "Hello " + name + ", Welcome to Hello Github!";
        
        /*这里的 conn 是一个 weak_ptr*/
        auto conn_sp = conn.lock();// 使用 weak_ptr 的 lock 方法获取一个 shared_ptr
        
        if (conn_sp) {
            /*操作完成,返回;std::move(ret) 为返回值*/
            conn_sp->pack_and_response(req_id, std::move(ret));
        }
    });
    
    thd.detach();
}

int main(){
    rpc_server server(9000, 6);
    
 server.register_handler<Async>("async_greet", async_greet);// 使用 Async 作为模板参数
    
    server.run();//启动服务端
    
    return EXIT_SUCCESS;
}

rest_rpcは、同じポートでの複数のサービスの登録をサポートします。次に例を示します。

server.register_handler("func_greet", hello);
server.register_handler("greet", &test_func::hello, &greeting);
server.register_handler("call_lambda", 
                        /*除 conn 外其他参数为可变参数*/
                        [&server](rpc_conn conn /*其他参数可有可无*/) {
                            std::cout << "Hello Github!" << std::endl;
                            // 返回值可有可无
                        });
// 其他服务等等

server.run();

3.3クライアントの作成

次のプロセスを実行するためにリモートサービス呼び出しを行うことができるクライアントを生成します。

  1. rpc_client オブジェクトのインスタンス化、サーバーアドレスとポートの設定

  2. サーバーに接続します

  3. 通話サービス

1)rpc_client

rpc_clientrest_rpcサーバーに接続するクライアントオブジェクトの場合、サーバーはサービスを呼び出し、メッセージをシリアル化し、メッセージを逆シリアル化し、その他の関数rest_rpcは名前空間あります。

rpc_clientオブジェクトを使用する必要性の例。次に、サーバーへの同期/非同期接続の使用connectまたはasync_connect方法を提供します

rpc_client client;

bool has_connected = client.connect("127.0.0.1", 9000);//同步连接,返回是否连接成功

client.async_connect("127.0.0.1", 9000);//异步连接,无返回值

もちろん、rpc_clientそれはまた提供されenable_auto_reconnectenable_auto_heartbeat機能します、それぞれの場合のために接続されたままです。

2)リモートサービスを呼び出す

rpc_clientオファーasync_callおよびcall非同期/同期呼び出しリモートサービスへの二つの方法、async_callおよびサポートcallbackfutureリターン・メッセージに対処するための2つの方法があり、このセクションでは、同期メソッド呼び出しを記述しますcall

callサービスメソッドを呼び出す際に、値に戻る場合はテンプレートパラメータを設定する必要があります。たとえば、リモートサービスは整数をcall<int>返します。指定しない場合は、戻り値のタイプを指定する必要があります。指定しない場合は戻り値がありません。

では、サーバの準備、各サービスの一部我々は、登録時に名前を持ち、リモート・サービス名で呼ばれ、今私たちが呼び出すことができると述べサーバー:部分書き込みの最初の例を

int main(){
    /* rest_rpc 在遇到错误(调用服务传入参数和远程服务需要参数不一致、连接失败等)时会抛出异常*/
    try{

        /*建立连接*/
        rpc_client client("127.0.0.1", 9000);// IP 地址,端口号
        /*设定超时 5s(不填默认为 3s),connect 超时返回 false,成功返回 true*/
        bool has_connected = client.connect(5);
        /*没有建立连接则退出程序*/
        if (!has_connected) {
            std::cout << "connect timeout" << std::endl;
            exit(-1);
        }

        /*调用远程服务,返回欢迎信息*/
        std::string result = client.call<std::string>("func_greet", "HG");// func_greet 为事先注册好的服务名,需要一个 name 参数,这里为 Hello Github 的缩写 HG
        std::cout << result << std::endl;

    }
    /*遇到连接错误、调用服务时参数不对等情况会抛出异常*/
    catch (const std::exception & e) {
        std::cout << e.what() << std::endl;
    }
    
    return EXIT_SUCCESS;
}

もちろん、一部の呼び出しはメッセージを返さない場合があります。これは直接の時間でありclient.call("xxx", ...)、メソッドの戻り値の型を呼び出しますvoid

3)リモートサービスを非同期で呼び出す

さまざまな理由でリモートサービスを呼び出すことがありますが、今回はrpc_client提供された非同期メソッド呼び出しを使用してasync_callデフォルトでコールバックモードになります。将来のモデルとして使用するタイムアウト時間のテンプレートパラメータを指定する必要があります。

コールバックモードでは、コールバック関数の仮パラメーターはルーチンのパラメーターと同じである必要があり、呼び出し後に追加する必要がありますclient.run()

/*默认为 call back 模式,模板参数代表 timeout 2000ms,async_call 参数顺序为 服务名, 回调函数, 调用服务需要的参数(数目类型不定)*/
/*timeout 不指定则默认为 5s,设定为 0 代表不检查 timeout */
client.async_call<2000>("async_greet", 
                  /*在远程服务返回时自动调用该回调函数,注意形参只能这样写*/
                  [&client](const boost::system::error_code & ec, string_view data) {
                        
                        auto str = as<std::string>(data);
                        std::cout << str << std::endl;
                   }, 
                  "HG");// echo 服务将传入的参数直接返回
client.run(); // 启动服务线程,等待返回

// 其余部分和 call 的使用方法一样

将来のモード:

auto f = client.async_call<FUTURE>("async_greet", "HG");

if (f.wait_for(std::chrono::milliseconds(50)) == std::future_status::timeout) {
    std::cout << "timeout" << std::endl;
}
else {
    auto ret = f.get().as<std::string>();// 转换为 string 对象,无返回值可以写 f.get().as()
    std::cout << ret << std::endl;
}

3.4シリアル化

rest_rpcを使用する場合、パラメーターが標準ライブラリ関連オブジェクトの場合、シリアル化メソッドを個別に指定する必要はありません。カスタムオブジェクトを使用する場合は、msgpackを使用してシリアル化メソッドを定義する必要があります。構造:

struct person {
 int id;
 std::string name;
 int age;
};

追加する必要がありますMSGPACK_DEFINE()

/*
注意:无论是服务端还是客户端都要进行这样的操作
客户端和服务端 MSGPACK_DEFINE() 中的填入的参数顺序必须一致,这一点和 msgpack 的序列化方式有
如客户端和服务端中 MSGPACK_DEFINE() 中参数顺序不一致可能会导致解包时发生错误
*/
struct person {
 int id;
 std::string name;
 int age;

 MSGPACK_DEFINE(id, name, age);//定义需要序列化的内容
};

同じことがオブジェクトにも当てはまります。

class person{
    private:
     int id;
        std::string name;
        int age;
    public:
     MSGPACK_DEFINE(id, name, age);//需要在 public 中
}

次に、パラメータタイプとしてpersonを使用できます。

4.機能:パブリッシュ/サブスクライブモデル

rest_rpcの主な機能は、パブリッシュ/サブスクライブモデルを提供することです。これは、クライアントとサーバー間でメッセージを継続的に送信する必要がある場合に非常に役立ちます。

サーバー側ではrpc_serverpublishorpublish_by_tokenメソッドを使用してサブスクリプションメッセージを公開するだけで済みます。トークンを使用する場合、サブスクライバーは同じトークンを使用してアクセスする必要があります。次に例を示します。

int main() {
    rpc_server server(9000, 6);

    std::thread broadcast([&server]() {
        while (true) {
            /*发布订阅消息,所有订阅了 greet 的客户端都可以获得消息*/
            server.publish("greet", "Hello GitHub!");
            /*只有订阅了 secret_greet 并且提供了 www.hellogithub.com 作为 token 才可以获得消息*/
            server.publish_by_token("secret_greet", "www.hellogithub.com", "Hello Github! this is secret message");

            std::this_thread::sleep_for(std::chrono::seconds(1));// 等待一秒
        }
    });

    server.run();//启动服务端

    return EXIT_SUCCESS;
}

クライアントrpc_client、このsubscribeメソッドを使用して次の  ことを行います。

void test_subscribe() {
    rpc_client client;

    client.enable_auto_reconnect();// 自动重连
    client.enable_auto_heartbeat();// 自动心跳包
    bool r = client.connect("127.0.0.1", 9000);
    if (!r) {
        return;
    }

    // 直接订阅,无 token
    client.subscribe("greet", [](string_view data) {
        std::cout << data << std::endl;
        });
    // 需要 token 才能正常获得订阅消息
    client.subscribe("secret_greet", "www.hellogithub.com", [](string_view data) {
        std::cout << data << std::endl;
        });
    
    client.run();// 不断运行
}

int main() {
    
    test_subscribe();

    return EXIT_SUCCESS;
}

1)サブスクライブ時にカスタムオブジェクトを転送する

送信するオブジェクトがある場合:

struct person {
 int id;
 std::string name;
 int age;

 MSGPACK_DEFINE(id, name, age);
};

サーバーは、これをパラメーターとして直接使用できます。次に例を示します。

person p{ 1, "tom", 20 };
server.publish("key", p);

クライアントデシリアライズする必要があります

client.subscribe("key", 
                 [](string_view data) {
                     msgpack_codec codec;
                     
                     person p = codec.unpack<person>(data.data(), data.size());
                     std::cout << p.name << std::endl;
                 });

ファイブ、ついに

RPCには、次のような多くの成熟した産業フレームワークがあります。

  • Google grpc

  • Baiduのbrpcなど。

ただし、rest_rpcと比較すると、構成と使用がより複雑です。初心者にとって、RPCのエントリプロジェクトとしてrest_rpcを使用することは非常に良い選択です。

この時点で、rest_rpcのほとんどの機能をマスターしたと思います。それでは、RPCサービスを開始します。

クリックしてフォローし、できるだけ早くプッシュを受け取ります

▼クリックして元の 提出物を 読む

おすすめ

転載: blog.csdn.net/a419240016/article/details/113931146