baidu-rpc可通过多种方式访问用ub搭建的服务。 # ubrpc (by protobuf) r31687后,baidu-rpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不依赖idl-compiler。(也可以让protobuf服务被ubrpc client访问,方法见[使用ubrpc的服务](nshead_service.md#id-实现NsheadService-使用ubrpc的服务))。 **步骤:** 1. 用[idl2proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/tools/idl2proto)把idl文件转化为proto文件,老版本idl2proto不会转化idl中的service,需要手动转化。 ```protobuf // Converted from echo.idl by public/mcpack2pb/idl2proto import "idl_options.proto"; option (idl_support) = true; option cc_generic_services = true; message EchoRequest { required string message = 1; } message EchoResponse { required string message = 1; } // 对于idl中多个request或response的方法,要建立一个包含所有request或response的消息。 // 这个例子中就是MultiRequests和MultiResponses。 message MultiRequests { required EchoRequest req1 = 1; required EchoRequest req2 = 2; } message MultiResponses { required EchoRequest res1 = 1; required EchoRequest res2 = 2; } service EchoService { // 对应idl中的void Echo(EchoRequest req, out EchoResponse res); rpc Echo(EchoRequest) returns (EchoResponse); // 对应idl中的uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2, out EchoResponse res1, out EchoResponse res2); rpc EchoWithMultiArgs(MultiRequests) returns (MultiResponses); } ``` 原先的echo.idl文件: ```idl struct EchoRequest { string message; }; struct EchoResponse { string message; }; service EchoService { void Echo(EchoRequest req, out EchoResponse res); uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2, out EchoResponse res1, out EchoResponse res2); }; ``` 2. 在COMAKE或BCLOUD中插入如下片段以使用代码生成插件。 注意--mcpack_out要和--cpp_out一致,你可以先设成--mcpack_out=.,执行comake2或bcloud后看错误信息中的--cpp_out的值,再把--mcpack_out设成一样的。 BCLOUD中要把`/public/mcpack2pb/protoc-gen-mcpack`替换成`/public/mcpack2pb/protoc-gen-mcpack**.forbcloud**`,并把ENV.WorkRoot()替换为WORKROOT的实际值。 ```python PROTOC(ENV.WorkRoot()+"/third-64/protobuf/bin/protoc") PROTOFLAGS("--plugin=protoc-gen-mcpack=" + ENV.WorkRoot() + "/public/mcpack2pb/protoc-gen-mcpack --mcpack_out=.") PROTOFLAGS('--proto_path=' + ENV.WorkRoot() + 'public/mcpack2pb/') PROTOFLAGS('--proto_path=' + ENV.WorkRoot() + 'third-64/protobuf/include/') ``` 3. 用channel发起访问。 idl不同于pb,允许有多个请求,我们先看只有一个请求的情况,和普通的pb访问基本上是一样的。 ```c++ #include brpc/channel.h> #include "echo.pb.h" ... brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack"; if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } EchoService_Stub stub(&channel); ... EchoRequest request; EchoResponse response; brpc::Controller cntl; request.set_message("hello world"); stub.Echo(&cntl, &request, &response, NULL); if (cntl.Failed()) { LOG(ERROR) << "Fail to send request, " << cntl.ErrorText(); return; } // 取response中的字段 // [idl] void Echo(EchoRequest req, out EchoResponse res); // ^ // response.message(); ``` 多个请求要设置一下set_idl_names。 ```c++ #include brpc/channel.h> #include "echo.pb.h" ... brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack"; if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } EchoService_Stub stub(&channel); ... MultiRequests multi_requests; MultiResponses multi_responses; brpc::Controller cntl; multi_requests.mutable_req1()->set_message("hello"); multi_requests.mutable_req2()->set_message("world"); cntl.set_idl_names(brpc::idl_multi_req_multi_res); stub.EchoWithMultiArgs(&cntl, &multi_requests, &multi_responses, NULL); if (cntl.Failed()) { LOG(ERROR) << "Fail to send request, " << cntl.ErrorText(); return; } // 取response中的字段 // [idl] uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2, // ^ out EchoResponse res1, out EchoResponse res2); // | ^ ^ // | multi_responses.res1().message(); | // | multi_responses.res2().message(); // cntl.idl_result(); ``` 例子详见[example/echo_c++_ubrpc_compack](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_ubrpc_compack/)。 # ubrpc (by baidu-rpc-ub) server端由public/ubrpc搭建,request/response使用idl文件描述字段,序列化格式是compack或mcpack_v2。 **步骤:** 1. 依赖[public/baidu-rpc-ub](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob)模块,在COMAKE中增加依赖:`CONFIGS('public/baidu-rpc-ub@ci-base')。`这个模块是baidu-rpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,baidu-rpc中的新特性会自动体现在这个模块中。 2. 编写一个proto文件,其中定义了service,名字和idl中的相同,但请求类型必须是baidu.rpc.UBRequest,回复类型必须是baidu.rpc.UBResponse。这两个类型定义在brpc/ub.proto中,使用时得import。 ```protobuf import "brpc/ub.proto"; // UBRequest, UBResponse option cc_generic_services = true; // Define UB service. request/response must be UBRequest/UBResponse service EchoService { rpc Echo(baidu.rpc.UBRequest) returns (baidu.rpc.UBResponse); }; ``` 3. 在COMAKE包含baidu-rpc-ub/src路径。 ```python # brpc/ub.proto的包含路径 PROTOC(ENV.WorkRoot()+"third-64/protobuf/bin/protoc") PROTOFLAGS("--proto_path=" + ENV.WorkRoot() + "public/baidu-rpc-ub/src/") ``` 4. 用法和访问其他协议类似:创建Channel,ChannelOptions.protocol为**brpc::PROTOCOL_NSHEAD_CLIENT**或**"nshead_client"**。request和response对象必须是baidu-rpc-ub提供的类型 ```c++ #include brpc/ub_call.h> ... brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client"; if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } EchoService_Stub stub(&channel); ... const int BUFSIZE = 1024 * 1024; // 1M char* buf_for_mempool = new char[BUFSIZE]; bsl::xmempool pool; if (pool.create(buf_for_mempool, BUFSIZE) != 0) { LOG(FATAL) << "Fail to create bsl::xmempool"; return -1; } // 构造UBRPC的request/response,idl结构体作为模块参数传入。为了构造idl结构,需要传入一个bsl::mempool brpc::UBRPCCompackRequest<example::EchoService_Echo_params> request(&pool); brpc::UBRPCCompackResponse<example::EchoService_Echo_response> response(&pool); // 设置字段 request.mutable_req()->set_message("hello world"); // 发起RPC brpc::Controller cntl; stub.Echo(&cntl, &request, &response, NULL); if (cntl.Failed()) { LOG(ERROR) << "Fail to Echo, " << cntl.ErrorText(); return; } // 取回复中的字段 response.result_params().res().message(); ... ``` 具体example代码可以参考[echo_c++_compack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_compack_ubrpc/),类似的还有[echo_c++_mcpack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_mcpack_ubrpc/)。 # nshead+idl server端是由public/ub搭建,通讯包组成为nshead+idl::compack/idl::mcpack(v2) 由于不需要指定service和method,无需编写proto文件,直接使用Channel.CallMethod方法发起RPC即可。请求包中的nshead可以填也可以不填,框架会补上正确的magic_num和body_len字段: ```c++ #include brpc/ub_call.h> ... brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client"; if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } ... // 构造UB的request/response,完全类似构造原先idl结构,传入一个bsl::mempool(变量pool) // 将类型作为模板传入,之后在使用上可以直接使用对应idl结构的接口 brpc::UBCompackRequest<example::EchoRequest> request(&pool); brpc::UBCompackResponse<example::EchoResponse> response(&pool); // Set `message' field of `EchoRequest' request.set_message("hello world"); // Set fields of the request nshead struct if needed request.mutable_nshead()->version = 99; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功 // Get `message' field of `EchoResponse' response.message(); ``` 具体example代码可以参考[echo_c++_mcpack_ub](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_mcpack_ub/),compack情况类似,不再赘述 # nshead+mcpack(非idl产生的) server端是由public/ub搭建,通讯包组成为nshead+mcpack包,但不是idl编译器生成的,RPC前需要先构造RawBuffer将其传入,然后获取mc_pack_t并按之前手工填写mcpack的方式操作: ```c++ #include brpc/ub_call.h> ... brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client"; if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } ... // 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool const int BUFSIZE = 10 * 1024 * 1024; brpc::RawBuffer req_buf(BUFSIZE); brpc::RawBuffer res_buf(BUFSIZE); // 传入RawBuffer来构造request和response brpc::UBRawMcpackRequest request(&req_buf); brpc::UBRawMcpackResponse response(&res_buf); // Fetch mc_pack_t and fill in variables mc_pack_t* req_pack = request.McpackHandle(); int ret = mc_pack_put_str(req_pack, "mystr", "hello world"); if (ret != 0) { LOG(FATAL) << "Failed to put string into mcpack: " << mc_pack_perror((long)req_pack) << (void*)req_pack; break; } // Set fields of the request nshead struct if needed request.mutable_nshead()->version = 99; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功 // Get response from response buffer const mc_pack_t* res_pack = response.McpackHandle(); mc_pack_get_str(res_pack, "mystr"); ``` 具体example代码可以参考[echo_c++_raw_mcpack](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_raw_mcpack/)。 # nshead+blob r32897后baidu-rpc直接支持用nshead+blob访问老server(而不用依赖baidu-rpc-ub)。example代码可以参考[nshead_extension_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/nshead_extension_c++/client.cpp)。 ```c++ #include brpc/nshead_message.h> ... brpc::Channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_NSHEAD; // or "nshead" if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } ... brpc::NsheadMessage request; brpc::NsheadMessage response; // Append message to `request' request.body.append("hello world"); // Set fields of the request nshead struct if needed request.head.version = 99; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); if (cntl.Failed()) { LOG(ERROR) << "Fail to access the server: " << cntl.ErrorText(); return -1; } // response.head and response.body contains nshead_t and blob respectively. ``` 或者用户也可以使用baidu-rpc-ub中的UBRawBufferRequest和UBRawBufferResponse来访问。example代码可以参考[echo_c++_raw_buffer](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_raw_buffer/)。 ```c++ brpc::Channel channel; brpc::ChannelOptions opt; opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client" if (channel.Init(..., &opt) != 0) { LOG(ERROR) << "Fail to init channel"; return -1; } ... // 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool const int BUFSIZE = 10 * 1024 * 1024; brpc::RawBuffer req_buf(BUFSIZE); brpc::RawBuffer res_buf(BUFSIZE); // 传入RawBuffer来构造request和response brpc::UBRawBufferRequest request(&req_buf); brpc::UBRawBufferResponse response(&res_buf); // Append message to `request' request.append("hello world"); // Set fields of the request nshead struct if needed request.mutable_nshead()->version = 99; brpc::Controller cntl; channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功 // Process response. response.data() is the buffer, response.size() is the length. ```