ub_client.md 12.6 KB
Newer Older
gejun's avatar
gejun committed
1
brpc可通过多种方式访问用ub搭建的服务。
2 3 4

# ubrpc (by protobuf)

gejun's avatar
gejun committed
5
r31687后,brpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不依赖idl-compiler。(也可以让protobuf服务被ubrpc client访问,方法见[使用ubrpc的服务](nshead_service.md#使用ubrpc的服务))。
6 7 8

**步骤:**

gejun's avatar
gejun committed
9
1.[idl2proto](https://github.com/brpc/brpc/blob/master/tools/idl2proto)把idl文件转化为proto文件,老版本idl2proto不会转化idl中的service,需要手动转化。
10 11

   ```protobuf
gejun's avatar
gejun committed
12
   // Converted from echo.idl by brpc/tools/idl2proto
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
   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);
   };
   ```

gejun's avatar
gejun committed
60
2. 插入如下片段以使用代码生成插件。
61

gejun's avatar
gejun committed
62
   BRPC_PATH代表brpc产出的路径(包含bin include等目录),PROTOBUF_INCLUDE_PATH代表protobuf的包含路径。注意--mcpack_out要和--cpp_out一致。
63

gejun's avatar
gejun committed
64 65
   ```shell
   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
66 67 68 69 70 71 72
   ```

3. channel发起访问。

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

   ```c++
gejun's avatar
gejun committed
73
   #include <brpc/channel.h>
74 75 76
   #include "echo.pb.h"
   ...
    
77 78 79
   brpc::Channel channel;
   brpc::ChannelOptions opt;
   opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack";
80 81 82 83 84 85 86 87 88
   if (channel.Init(..., &opt) != 0) {
       LOG(ERROR) << "Fail to init channel";
       return -1;
   }
   EchoService_Stub stub(&channel);
   ...
    
   EchoRequest request;
   EchoResponse response;
89
   brpc::Controller cntl;
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    
   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++
gejun's avatar
gejun committed
108
   #include <brpc/channel.h>
109 110 111
   #include "echo.pb.h"
   ...
    
112 113 114
   brpc::Channel channel;
   brpc::ChannelOptions opt;
   opt.protocol = brpc::PROTOCOL_UBRPC_COMPACK; // or "ubrpc_compack";
115 116 117 118 119 120 121 122 123
   if (channel.Init(..., &opt) != 0) {
       LOG(ERROR) << "Fail to init channel";
       return -1;
   }
   EchoService_Stub stub(&channel);
   ...
    
   MultiRequests multi_requests;
   MultiResponses multi_responses;
124
   brpc::Controller cntl;
125 126 127
    
   multi_requests.mutable_req1()->set_message("hello");
   multi_requests.mutable_req2()->set_message("world");
128
   cntl.set_idl_names(brpc::idl_multi_req_multi_res);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
   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();
   ```

gejun's avatar
gejun committed
144
   例子详见[example/echo_c++_ubrpc_compack](https://github.com/brpc/brpc/blob/master/example/echo_c++_ubrpc_compack/)。
145 146 147 148 149 150 151

# ubrpc (by baidu-rpc-ub)

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

**步骤:**

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

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

   ```protobuf
157
   import "brpc/ub.proto";              // UBRequest, UBResponse
158 159 160 161 162 163 164 165 166 167
   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
168
   # brpc/ub.proto的包含路径
169 170 171 172
   PROTOC(ENV.WorkRoot()+"third-64/protobuf/bin/protoc")
   PROTOFLAGS("--proto_path=" + ENV.WorkRoot() + "public/baidu-rpc-ub/src/")
   ```

173
4. 用法和访问其他协议类似:创建ChannelChannelOptions.protocol为**brpc::PROTOCOL_NSHEAD_CLIENT**或**"nshead_client"**。requestresponse对象必须是baidu-rpc-ub提供的类型
174 175

   ```c++
gejun's avatar
gejun committed
176
   #include <brpc/ub_call.h>
177 178
   ...
       
179 180 181
   brpc::Channel channel;
   brpc::ChannelOptions opt;
   opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
   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
198 199
   brpc::UBRPCCompackRequest<example::EchoService_Echo_params> request(&pool);
   brpc::UBRPCCompackResponse<example::EchoService_Echo_response> response(&pool);
200 201 202 203 204
    
   // 设置字段
   request.mutable_req()->set_message("hello world");
    
   // 发起RPC
205
   brpc::Controller cntl;
206 207 208 209 210 211 212 213 214 215 216
   stub.Echo(&cntl, &request, &response, NULL);
       
   if (cntl.Failed()) {
       LOG(ERROR) << "Fail to Echo, " << cntl.ErrorText();
       return;
   }
   // 取回复中的字段
   response.result_params().res().message();
   ...
   ```

gejun's avatar
gejun committed
217
   具体example代码可以参考[echo_c++_compack_ubrpc](https://github.com/brpc/brpc/tree/master/example/echo_c++_compack_ubrpc/),类似的还有[echo_c++_mcpack_ubrpc](https://github.com/brpc/brpc/tree/master/example/echo_c++_mcpack_ubrpc/)。
218 219 220 221 222 223 224 225

# nshead+idl

server端是由public/ub搭建,通讯包组成为nshead+idl::compack/idl::mcpack(v2)

由于不需要指定servicemethod,无需编写proto文件,直接使用Channel.CallMethod方法发起RPC即可。请求包中的nshead可以填也可以不填,框架会补上正确的magic_numbody_len字段:

```c++
gejun's avatar
gejun committed
226
#include <brpc/ub_call.h>
227 228
...
 
229 230 231
brpc::Channel channel;
brpc::ChannelOptions opt;
opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
232 233 234 235 236 237 238 239 240
 
if (channel.Init(..., &opt) != 0) {
    LOG(ERROR) << "Fail to init channel";
    return -1;
}
...
 
// 构造UB的request/response,完全类似构造原先idl结构,传入一个bsl::mempool(变量pool)
// 将类型作为模板传入,之后在使用上可以直接使用对应idl结构的接口
241 242
brpc::UBCompackRequest<example::EchoRequest> request(&pool);
brpc::UBCompackResponse<example::EchoResponse> response(&pool);
243 244 245 246 247 248
 
// Set `message' field of `EchoRequest'
request.set_message("hello world");
// Set fields of the request nshead struct if needed
request.mutable_nshead()->version = 99;
 
249
brpc::Controller cntl;
250 251 252 253 254 255
channel.CallMethod(NULL, &cntl, &request, &response, NULL);    // 假设channel已经通过之前所述方法Init成功
 
// Get `message' field of `EchoResponse'
response.message();
```

gejun's avatar
gejun committed
256
具体example代码可以参考[echo_c++_mcpack_ub](https://github.com/brpc/brpc/blob/master/example/echo_c++_mcpack_ub/),compack情况类似,不再赘述
257 258 259 260 261 262

# nshead+mcpack(非idl产生的)

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

```c++
gejun's avatar
gejun committed
263
#include <brpc/ub_call.h>
264 265
...
 
266 267 268
brpc::Channel channel;
brpc::ChannelOptions opt;
opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client";
269 270 271 272 273 274 275 276
if (channel.Init(..., &opt) != 0) {
    LOG(ERROR) << "Fail to init channel";
    return -1;
}
...
 
// 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool
const int BUFSIZE = 10 * 1024 * 1024;
277 278
brpc::RawBuffer req_buf(BUFSIZE);
brpc::RawBuffer res_buf(BUFSIZE);
279 280
 
// 传入RawBuffer来构造request和response
281 282
brpc::UBRawMcpackRequest request(&req_buf);
brpc::UBRawMcpackResponse response(&res_buf);
283 284 285 286 287 288 289 290 291 292 293 294
         
// 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;
 
295
brpc::Controller cntl;
296 297 298 299 300 301 302
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");
```

gejun's avatar
gejun committed
303
具体example代码可以参考[echo_c++_raw_mcpack](https://github.com/brpc/brpc/blob/master/example/echo_c++_raw_mcpack/)。
304 305 306

# nshead+blob

gejun's avatar
gejun committed
307
r32897后brpc直接支持用nshead+blob访问老server(而不用依赖baidu-rpc-ub)。example代码可以参考[nshead_extension_c++](https://github.com/brpc/brpc/blob/master/example/nshead_extension_c++/client.cpp)。
308 309

```c++
gejun's avatar
gejun committed
310
#include <brpc/nshead_message.h>
311 312
...
 
313 314 315
brpc::Channel;
brpc::ChannelOptions opt;
opt.protocol = brpc::PROTOCOL_NSHEAD; // or "nshead"
316 317 318 319 320
if (channel.Init(..., &opt) != 0) {
    LOG(ERROR) << "Fail to init channel";
    return -1;
} 
...
321 322
brpc::NsheadMessage request;
brpc::NsheadMessage response;
323 324 325 326 327 328 329
       
// Append message to `request'
request.body.append("hello world");
// Set fields of the request nshead struct if needed
request.head.version = 99;
 
 
330
brpc::Controller cntl;
331 332 333 334 335 336 337 338 339
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.
```

gejun's avatar
gejun committed
340
或者用户也可以使用baidu-rpc-ub中的UBRawBufferRequest和UBRawBufferResponse来访问。example代码可以参考[echo_c++_raw_buffer](https://github.com/brpc/brpc/blob/master/example/echo_c++_raw_buffer/)。
341 342

```c++
343 344 345
brpc::Channel channel;
brpc::ChannelOptions opt;
opt.protocol = brpc::PROTOCOL_NSHEAD_CLIENT; // or "nshead_client"
346 347 348 349 350 351 352 353
if (channel.Init(..., &opt) != 0) {
    LOG(ERROR) << "Fail to init channel";
    return -1;
}
...
 
// 构造RawBuffer,一次RPC结束后RawBuffer可以复用,类似于bsl::mempool
const int BUFSIZE = 10 * 1024 * 1024;
354 355
brpc::RawBuffer req_buf(BUFSIZE);
brpc::RawBuffer res_buf(BUFSIZE);
356 357
 
// 传入RawBuffer来构造request和response
358 359
brpc::UBRawBufferRequest request(&req_buf);
brpc::UBRawBufferResponse response(&res_buf);
360 361 362 363 364 365
         
// Append message to `request'
request.append("hello world");
// Set fields of the request nshead struct if needed
request.mutable_nshead()->version = 99;
 
366
brpc::Controller cntl;
367 368 369 370
channel.CallMethod(NULL, &cntl, &request, &response, NULL);    // 假设channel已经通过之前所述方法Init成功
 
// Process response. response.data() is the buffer, response.size() is the length.
```