使用时需要先实例化一个 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 方法的例子:
/*使用 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_clientrpc_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 两种处理返回消息的方法,这部分介绍 同步 调用方法 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", ...) 即可,此时 call 方法返回类型为 void。
3)异步调用远程服务