【brpc学习实战二】brpc client构建基本流程

client基本概念及学习指南

https://github.com/luozesong/brpc/blob/master/docs/cn/client.md

一、编写proto

这里与服务一致,实际开发中需要双端共同确定proto内容;

二、初始化channel

rpc channel可以视为socket编程中的client对象

定义一个channel,Channel可以被所有线程共用,你不需要为每个线程创建独立的Channel,也不需要用锁互斥。不过Channel的创建和Init并不是线程安全的,请确保在Init成功后再被多线程访问,在没有线程访问后再析构

三、初始化channel options

填充连接相关的的参数如超时、协议、最大重试次数、负载均衡算法等

四、channel init with options

初始化channel

五、初始化自定义service_stub

protobuf会为我们定义的rpc 函数生成一个xxx_stub用来和服务交互。我们需要用自定义service_stub初始化我们的客户端对象。如果proto没定义rpc 服务,可以用Channel.CallMethod。

六、发起访问

一般来说,我们不直接调用Channel.CallMethod,而是通过protobuf生成的桩XXX_Stub,过程更像是"调用函数"。stub内没什么成员变量,建议在栈上创建和使用,而不必new,当然你也可以把stub存下来复用。Channel::CallMethod和stub访问都是线程安全的,可以被所有线程同时访问。

XXX_Stub stub(&channel);

stub.some_method(controller, request, response, done);

这里的some_method为在proto中定义的rpc服务经过编译后得到的抽象接口,像service_stub.ParallelSearchPlans。

如果我们没定义rpc服务,用brpc内部已定义好的服务时,需要用到callmethod()发起请求。而我们可以通过addchannel组装多个请求,一次调用,可以用在提升服务的性能。下面是一个实际示例

cpp 复制代码
while (request_num--) {
        pchan.AddChannel(channel, baidu::rpc::DOESNT_OWN_CHANNEL, rpc_call_mapper, rpc_response_merger);
    }
    
    uint64_t logid = UII_GET_LOGID();
    cntl->set_log_id(logid);
    AccessLogGuard access_log_guard(service_name.c_str(), "nshead", req, res, cntl);

    pchan.CallMethod(NULL, cntl, req, res, baidu::rpc::DoNothing());
    if (cntl->Failed()) {
        UII_LOG_WARNING("Fail to access service:%s Error:%s", service_name.c_str(), cntl->ErrorText().c_str());
        return -1;
    }

上面代码使用rpc::DoNothing()实现了一个半同步的请求响应,阻塞到所有的请求返回。

七、返回处理

我们发起访问后,可以根据controler调用cntl->Failed()判断请求是否成功。若成功,就可以对res响应进行处理。

八、基础客户端案例及释义

cpp 复制代码
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

// A client sending requests to server every 1 second.

#include <gflags/gflags.h>
#include <butil/logging.h>
#include <butil/time.h>
#include <brpc/channel.h>
#include "echo.pb.h"

DEFINE_string(attachment, "", "Carry this along with requests");
DEFINE_string(protocol, "baidu_std", "Protocol type. Defined in src/brpc/options.proto");
DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short");
DEFINE_string(server, "0.0.0.0:8000", "IP Address of server");
DEFINE_string(load_balancer, "", "The algorithm for load balancing");
DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)"); 
DEFINE_int32(interval_ms, 1000, "Milliseconds between consecutive requests");

int main(int argc, char* argv[]) {
    // Parse gflags. We recommend you to use gflags as well.
    GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);
    
    brpc::Channel channel;
    
    // Initialize the channel, NULL means using default options.
    brpc::ChannelOptions options;
    options.protocol = FLAGS_protocol;
    options.connection_type = FLAGS_connection_type;
    options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/;
    options.max_retry = FLAGS_max_retry;
    if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) {
        LOG(ERROR) << "Fail to initialize channel";
        return -1;
    }

    // Normally, you should not call a Channel directly, but instead construct
    // a stub Service wrapping it. stub can be shared by all threads as well.
    example::EchoService_Stub stub(&channel);

    // Send a request and wait for the response every 1 second.
    int log_id = 0;
    while (!brpc::IsAskedToQuit()) {
        // We will receive response synchronously, safe to put variables
        // on stack.
        example::EchoRequest request;
        example::EchoResponse response;
        brpc::Controller cntl;

        request.set_message("hello world");

        cntl.set_log_id(log_id ++);  // set by user
        // Set attachment which is wired to network directly instead of 
        // being serialized into protobuf messages.
        cntl.request_attachment().append(FLAGS_attachment);

        // Because `done'(last parameter) is NULL, this function waits until
        // the response comes back or error occurs(including timedout).
        stub.Echo(&cntl, &request, &response, NULL);
        if (!cntl.Failed()) {
            LOG(INFO) << "Received response from " << cntl.remote_side()
                << " to " << cntl.local_side()
                << ": " << response.message() << " (attached="
                << cntl.response_attachment() << ")"
                << " latency=" << cntl.latency_us() << "us";
        } else {
            LOG(WARNING) << cntl.ErrorText();
        }
        usleep(FLAGS_interval_ms * 1000L);
    }

    LOG(INFO) << "EchoClient is going to quit";
    return 0;
}
相关推荐
骆晨学长几秒前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
LyaJpunov2 分钟前
C++中move和forword的区别
开发语言·c++
AskHarries6 分钟前
利用反射实现动态代理
java·后端·reflect
程序猿练习生7 分钟前
C++速通LeetCode中等第9题-合并区间
开发语言·c++·leetcode
z千鑫15 分钟前
【人工智能】如何利用AI轻松将java,c++等代码转换为Python语言?程序员必读
java·c++·人工智能·gpt·agent·ai编程·ai工具
一名路过的小码农17 分钟前
C/C++动态库函数导出 windows
c语言·开发语言·c++
Flying_Fish_roe29 分钟前
Spring Boot-Session管理问题
java·spring boot·后端
Ddddddd_1581 小时前
C++ | Leetcode C++题解之第416题分割等和子集
c++·leetcode·题解
hai405871 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
编程版小新1 小时前
C++初阶:STL详解(四)——vector迭代器失效问题
开发语言·c++·迭代器·vector·迭代器失效