brpc可通过多种方式访问用ub搭建的服务。

ubrpc (by protobuf)

r31687后,brpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不依赖idl-compiler。(也可以让protobuf服务被ubrpc client访问,方法见使用ubrpc的服务)。

步骤:

  • idl2proto把idl文件转化为proto文件,老版本idl2proto不会转化idl中的service,需要手动转化。
  1. // Converted from echo.idl by brpc/tools/idl2proto
  2. import "idl_options.proto";
  3. option (idl_support) = true;
  4. option cc_generic_services = true;
  5. message EchoRequest {
  6. required string message = 1;
  7. }
  8. message EchoResponse {
  9. required string message = 1;
  10. }
  11.  
  12. // 对于idl中多个request或response的方法,要建立一个包含所有request或response的消息。
  13. // 这个例子中就是MultiRequests和MultiResponses。
  14. message MultiRequests {
  15. required EchoRequest req1 = 1;
  16. required EchoRequest req2 = 2;
  17. }
  18. message MultiResponses {
  19. required EchoRequest res1 = 1;
  20. required EchoRequest res2 = 2;
  21. }
  22.  
  23. service EchoService {
  24. // 对应idl中的void Echo(EchoRequest req, out EchoResponse res);
  25. rpc Echo(EchoRequest) returns (EchoResponse);
  26.  
  27. // 对应idl中的uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2, out EchoResponse res1, out EchoResponse res2);
  28. rpc EchoWithMultiArgs(MultiRequests) returns (MultiResponses);
  29. }

原先的echo.idl文件:

  1. struct EchoRequest {
  2. string message;
  3. };
  4.  
  5. struct EchoResponse {
  6. string message;
  7. };
  8.  
  9. service EchoService {
  10. void Echo(EchoRequest req, out EchoResponse res);
  11. uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2, out EchoResponse res1, out EchoResponse res2);
  12. };
  • 插入如下片段以使用代码生成插件。

BRPC_PATH代表brpc产出的路径(包含bin include等目录),PROTOBUF_INCLUDE_PATH代表protobuf的包含路径。注意—mcpack_out要和—cpp_out一致。

  1. protoc --plugin=protoc-gen-mcpack=$BRPC_PATH/bin/protoc-gen-mcpack --cpp_out=. --mcpack_out=. --proto_path=$BRPC_PATH/include --proto_path=PROTOBUF_INCLUDE_PATH
  • 用channel发起访问。

idl不同于pb,允许有多个请求,我们先看只有一个请求的情况,和普通的pb访问基本上是一样的。

  1. #include <brpc/channel.h>
  2. #include "echo.pb.h"
  3. ...
  4.  
  5. brpc::Channel channel;
  6. brpc::ChannelOptions opt;
  7. opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack";
  8. if (channel.Init(..., &opt) != 0) {
  9. LOG(ERROR) << "Fail to init channel";
  10. return -1;
  11. }
  12. EchoService_Stub stub(&channel);
  13. ...
  14.  
  15. EchoRequest request;
  16. EchoResponse response;
  17. brpc::Controller cntl;
  18.  
  19. request.set_message("hello world");
  20.  
  21. stub.Echo(&cntl, &request, &response, NULL);
  22.  
  23. if (cntl.Failed()) {
  24. LOG(ERROR) << "Fail to send request, " << cntl.ErrorText();
  25. return;
  26. }
  27. // 取response中的字段
  28. // [idl] void Echo(EchoRequest req, out EchoResponse res);
  29. // ^
  30. // response.message();

多个请求要设置一下set_idl_names。

  1. #include <brpc/channel.h>
  2. #include "echo.pb.h"
  3. ...
  4.  
  5. brpc::Channel channel;
  6. brpc::ChannelOptions opt;
  7. opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack";
  8. if (channel.Init(..., &opt) != 0) {
  9. LOG(ERROR) << "Fail to init channel";
  10. return -1;
  11. }
  12. EchoService_Stub stub(&channel);
  13. ...
  14.  
  15. MultiRequests multi_requests;
  16. MultiResponses multi_responses;
  17. brpc::Controller cntl;
  18.  
  19. multi_requests.mutable_req1()->set_message("hello");
  20. multi_requests.mutable_req2()->set_message("world");
  21. cntl.set_idl_names(brpc::idl_multi_req_multi_res);
  22. stub.EchoWithMultiArgs(&cntl, &multi_requests, &multi_responses, NULL);
  23.  
  24. if (cntl.Failed()) {
  25. LOG(ERROR) << "Fail to send request, " << cntl.ErrorText();
  26. return;
  27. }
  28. // 取response中的字段
  29. // [idl] uint32_t EchoWithMultiArgs(EchoRequest req1, EchoRequest req2,
  30. // ^ out EchoResponse res1, out EchoResponse res2);
  31. // | ^ ^
  32. // | multi_responses.res1().message(); |
  33. // | multi_responses.res2().message();
  34. // cntl.idl_result();

例子详见example/echo_c++_ubrpc_compack

ubrpc (by baidu-rpc-ub)

server端由public/ubrpc搭建,request/response使用idl文件描述字段,序列化格式是compack或mcpack_v2。

步骤:

  • 依赖public/baidu-rpc-ub模块,这个模块是brpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,brpc中的新特性会自动体现在这个模块中。

  • 编写一个proto文件,其中定义了service,名字和idl中的相同,但请求类型必须是baidu.rpc.UBRequest,回复类型必须是baidu.rpc.UBResponse。这两个类型定义在brpc/ub.proto中,使用时得import。

  1. import "brpc/ub.proto"; // UBRequest, UBResponse
  2. option cc_generic_services = true;
  3. // Define UB service. request/response must be UBRequest/UBResponse
  4. service EchoService {
  5. rpc Echo(baidu.rpc.UBRequest) returns (baidu.rpc.UBResponse);
  6. };
  • 在COMAKE包含baidu-rpc-ub/src路径。
  1. # brpc/ub.proto的包含路径
  2. PROTOC(ENV.WorkRoot()+"third-64/protobuf/bin/protoc")
  3. PROTOFLAGS("--proto_path=" + ENV.WorkRoot() + "public/baidu-rpc-ub/src/")
  • 用法和访问其他协议类似:创建Channel,ChannelOptions.protocol为brpc::PROTOCOL_NSHEAD_CLIENT"nshead_client"。request和response对象必须是baidu-rpc-ub提供的类型
  1. #include <brpc/ub_call.h>
  2. ...
  3.  
  4. brpc::Channel channel;
  5. brpc::ChannelOptions opt;
  6. opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
  7. if (channel.Init(..., &opt) != 0) {
  8. LOG(ERROR) << "Fail to init channel";
  9. return -1;
  10. }
  11. EchoService_Stub stub(&channel);
  12. ...
  13.  
  14. const int BUFSIZE = 1024 * 1024; // 1M
  15. char* buf_for_mempool = new char[BUFSIZE];
  16. bsl::xmempool pool;
  17. if (pool.create(buf_for_mempool, BUFSIZE) != 0) {
  18. LOG(FATAL) << "Fail to create bsl::xmempool";
  19. return -1;
  20. }
  21.  
  22. // 构造UBRPC的request/response,idl结构体作为模块参数传入。为了构造idl结构,需要传入一个bsl::mempool
  23. brpc::UBRPCCompackRequest<example::EchoService_Echo_params> request(&pool);
  24. brpc::UBRPCCompackResponse<example::EchoService_Echo_response> response(&pool);
  25.  
  26. // 设置字段
  27. request.mutable_req()->set_message("hello world");
  28.  
  29. // 发起RPC
  30. brpc::Controller cntl;
  31. stub.Echo(&cntl, &request, &response, NULL);
  32.  
  33. if (cntl.Failed()) {
  34. LOG(ERROR) << "Fail to Echo, " << cntl.ErrorText();
  35. return;
  36. }
  37. // 取回复中的字段
  38. response.result_params().res().message();
  39. ...

具体example代码可以参考echo_c++_compack_ubrpc,类似的还有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字段:

  1. #include <brpc/ub_call.h>
  2. ...
  3.  
  4. brpc::Channel channel;
  5. brpc::ChannelOptions opt;
  6. opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
  7.  
  8. if (channel.Init(..., &opt) != 0) {
  9. LOG(ERROR) << "Fail to init channel";
  10. return -1;
  11. }
  12. ...
  13.  
  14. // 构造UB的request/response,完全类似构造原先idl结构,传入一个bsl::mempool(变量pool)
  15. // 将类型作为模板传入,之后在使用上可以直接使用对应idl结构的接口
  16. brpc::UBCompackRequest<example::EchoRequest> request(&pool);
  17. brpc::UBCompackResponse<example::EchoResponse> response(&pool);
  18.  
  19. // Set `message' field of `EchoRequest'
  20. request.set_message("hello world");
  21. // Set fields of the request nshead struct if needed
  22. request.mutable_nshead()->version = 99;
  23.  
  24. brpc::Controller cntl;
  25. channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功
  26.  
  27. // Get `message' field of `EchoResponse'
  28. response.message();

具体example代码可以参考echo_c++_mcpack_ub,compack情况类似,不再赘述

nshead+mcpack(非idl产生的)

server端是由public/ub搭建,通讯包组成为nshead+mcpack包,但不是idl编译器生成的,RPC前需要先构造RawBuffer将其传入,然后获取mc_pack_t并按之前手工填写mcpack的方式操作:

  1. #include <brpc/ub_call.h>
  2. ...
  3.  
  4. brpc::Channel channel;
  5. brpc::ChannelOptions opt;
  6. opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
  7. if (channel.Init(..., &opt) != 0) {
  8. LOG(ERROR) << "Fail to init channel";
  9. return -1;
  10. }
  11. ...
  12.  
  13. // 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool
  14. const int BUFSIZE = 10 * 1024 * 1024;
  15. brpc::RawBuffer req_buf(BUFSIZE);
  16. brpc::RawBuffer res_buf(BUFSIZE);
  17.  
  18. // 传入RawBuffer来构造request和response
  19. brpc::UBRawMcpackRequest request(&req_buf);
  20. brpc::UBRawMcpackResponse response(&res_buf);
  21.  
  22. // Fetch mc_pack_t and fill in variables
  23. mc_pack_t* req_pack = request.McpackHandle();
  24. int ret = mc_pack_put_str(req_pack, "mystr", "hello world");
  25. if (ret != 0) {
  26. LOG(FATAL) << "Failed to put string into mcpack: "
  27. << mc_pack_perror((long)req_pack) << (void*)req_pack;
  28. break;
  29. }
  30. // Set fields of the request nshead struct if needed
  31. request.mutable_nshead()->version = 99;
  32.  
  33. brpc::Controller cntl;
  34. channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功
  35.  
  36. // Get response from response buffer
  37. const mc_pack_t* res_pack = response.McpackHandle();
  38. mc_pack_get_str(res_pack, "mystr");

具体example代码可以参考echo_c++_raw_mcpack

nshead+blob

r32897后brpc直接支持用nshead+blob访问老server(而不用依赖baidu-rpc-ub)。example代码可以参考nshead_extension_c++

  1. #include <brpc/nshead_message.h>
  2. ...
  3.  
  4. brpc::Channel;
  5. brpc::ChannelOptions opt;
  6. opt.protocol = brpc::PROTOCOL_NSHEAD; // or "nshead"
  7. if (channel.Init(..., &opt) != 0) {
  8. LOG(ERROR) << "Fail to init channel";
  9. return -1;
  10. }
  11. ...
  12. brpc::NsheadMessage request;
  13. brpc::NsheadMessage response;
  14.  
  15. // Append message to `request'
  16. request.body.append("hello world");
  17. // Set fields of the request nshead struct if needed
  18. request.head.version = 99;
  19.  
  20.  
  21. brpc::Controller cntl;
  22. channel.CallMethod(NULL, &cntl, &request, &response, NULL);
  23.  
  24. if (cntl.Failed()) {
  25. LOG(ERROR) << "Fail to access the server: " << cntl.ErrorText();
  26. return -1;
  27. }
  28. // response.head and response.body contains nshead_t and blob respectively.

或者用户也可以使用baidu-rpc-ub中的UBRawBufferRequest和UBRawBufferResponse来访问。example代码可以参考echo_c++_raw_buffer

  1. brpc::Channel channel;
  2. brpc::ChannelOptions opt;
  3. opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client"
  4. if (channel.Init(..., &opt) != 0) {
  5. LOG(ERROR) << "Fail to init channel";
  6. return -1;
  7. }
  8. ...
  9.  
  10. // 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool
  11. const int BUFSIZE = 10 * 1024 * 1024;
  12. brpc::RawBuffer req_buf(BUFSIZE);
  13. brpc::RawBuffer res_buf(BUFSIZE);
  14.  
  15. // 传入RawBuffer来构造request和response
  16. brpc::UBRawBufferRequest request(&req_buf);
  17. brpc::UBRawBufferResponse response(&res_buf);
  18.  
  19. // Append message to `request'
  20. request.append("hello world");
  21. // Set fields of the request nshead struct if needed
  22. request.mutable_nshead()->version = 99;
  23.  
  24. brpc::Controller cntl;
  25. channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel已经通过之前所述方法Init成功
  26.  
  27. // Process response. response.data() is the buffer, response.size() is the length.