有些时候我们调用的远程服务由于各种原因需要一些时间才能返回,这时候可以使用 rpc_client 提供的异步调用方法 async_call ,它默认为 callback 模式,模板参数为 timeout 时间,如想要使用 future 模式则需要特别指定。
callback 模式,回调函数形参要与例程中一样,在调用之后需要加上 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 的使用方法一样Future 模式:
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 作为参数类型进行使用。
四、特点:发布/订阅模式rest_rpc 的一大特色就是提供了 发布-订阅 模式,这个模式在客户端和服务端之间需要不停传输消息时非常有用。
服务端 只需要使用 rpc_server 的 publish 或者 publish_by_token 方法即可发布一条订阅消息,其中如果使用 token 则订阅者需要使用相同的 token 才能访问,例如:
int main() { rpc_server server(9000, 6); std::thread broadcast([&server]() { while (true) { /*发布订阅消息,所有订阅了 greet 的客户端都可以获得消息*/ server.publish("greet", "Hello GitHub!"); /*只有订阅了 secret_greet 并且提供了 作为 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);