【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;
}
相关推荐
Lumbrologist13 分钟前
【C++】零基础入门 · 第 2 节:变量、基本数据类型与输入输出
java·开发语言·c++
XX風17 分钟前
CMake / Make / Ninja / MSVC / GCC / Clang / MSBuild —— 完整体系化理解
c++
Peter·Pan爱编程1 小时前
10. new_delete 不是 malloc_free 的包装
c++·人工智能·算法
IT_陈寒1 小时前
Vue的computed属性怎么突然不更新了?
前端·人工智能·后端
invicinble1 小时前
spring提供的其他机制
java·后端·spring
还是鼠鼠1 小时前
AI掘金头条新闻系统 (Toutiao News)-用户注册-创建用户
后端·python·mysql·fastapi·web
李广坤2 小时前
别再把 Filter、Interceptor 和 AOP 混为一谈了!从接口加解密谈 Spring 纵深架构设计
后端
我是一颗柠檬2 小时前
【MySQL全面教学】MySQL条件查询与排序Day4(2026年)
数据库·后端·mysql
她的男孩2 小时前
后台权限不只是菜单隐藏:Forge Admin 的 RBAC 权限链路拆解
java·后端·架构
苏三说技术2 小时前
IntelliJ IDEA 从卡顿到起飞,只用改这些。。。
后端