この記事は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には多くの利点があります。
使いやすい
サブスクリプションモードをサポート
許可
future
とcallback
2種類の非同期呼び出しインターフェイス、関心のあるさまざまな人々に会う
2.クイックスタート
rest_rpc
Boost
使用前の依存関係は適切にインストールする必要があります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/client
てexamples/server
直接使用されるbasic_client.vcxproj
かbasic_server.vcxproj
、次のように直接公式ルーチンの操作結果にコンパイルされます。
注:プロジェクトには
Boost/asio
サポートBoost
が必要です。インストールされていない場合は、適切にインストールBoost
する必要がありBoost
、プロジェクトに追加されます。
プロジェクトに追加するBoost
方法は次のとおりです。
プロジェクトを開いた後
项目
、メニューバーの→をクリックします属性
(ショートカットキーAlt
+F7
)左を選択し
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サーバーの作成
サービスを提供できるクライアントを生成するには、いくつかのプロセスがあります。
rpc_server
オブジェクトのインスタンス化、リスニングポートの設定およびその他のプロパティサーバーが提供するサービスを定義するサービス機能の登録
サービス開始
1)rpc_server
rpc_server
rest_rpc
サーバーオブジェクトの場合、rest_rpc::rpc_service
名前空間にあるサーバーのサービス、パブリッシュ/サブスクライブ、スレッドプール管理、およびその他の基本機能の登録を担当します。
rpc_server
オブジェクトのインスタンス化を使用して、リスニングポート、スレッドプールサイズを提供する必要があります。次に例を示します。
rpc_server server(9000, 6); // 监听 9000 端口,线程池大小为 6
2)サーバーの登録と起動
rpc_server
register_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_handler
、Async
テンプレートパラメーター(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クライアントの作成
次のプロセスを実行するためにリモートサービス呼び出しを行うことができるクライアントを生成します。
rpc_client
オブジェクトのインスタンス化、サーバーアドレスとポートの設定サーバーに接続します
通話サービス
1)rpc_client
rpc_client
rest_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_reconnect
、enable_auto_heartbeat
機能します、それぞれの場合のために接続されたままです。
2)リモートサービスを呼び出す
rpc_client
オファーasync_call
およびcall
非同期/同期呼び出しリモートサービスへの二つの方法、async_call
およびサポートcallback
とfuture
リターン・メッセージに対処するための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_server
、publish
orpublish_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サービスを開始します。
クリックしてフォローし、できるだけ早くプッシュを受け取ります
▼クリックして元の 提出物を 読む