• protobuf和brpc


    1.Protobuf简介

    https://izualzhy.cn/demo-protobuf-rpc

    Protobuf(Google Protocol Buffers)提供一种灵活、高效、自动化的机制,用于序列化结构数据

    • Protobuf作用与XML、json类似,但它是二进制编码格式,所以性能更好。
    • 有代码生成机制,易于使用。
    
    
    syntax="proto2";
    package example;

    //请求、回复、服务的接口均定义在proto文件中。
    //告诉protoc要生成C++ Service基类
    option cc_generic_services = true;

    message EchoRequest {
        required string message = 1;
    };

    message EchoResponse {
        required string message = 1;
    };

    service EchoService {
        rpc Echo(EchoRequest) returns (EchoResponse);
    };
     

    定义message来指定所需要序列化的数据格式。每一个message都是一个小的信息逻辑单元,包含一系列的name-value值对。

    2.example echo_C++

    按照https://github.com/apache/incubator-brpc/blob/master/docs/cn/getting_started.md编译brpc的echo_c++,echo.pb.cc是中间文件会删除,就算中止make也会删除中间文件:

    make: *** Deleting intermediate file `echo.pb.cc'
    //或
    rm echo.pb.cc

    所以无法看到具体的EchoService_Stub类的实现,但这也说明不需要去关心Stub具体是如何做的?直接就实现了端到端?

    可以手动使用protoc命令生成.h和.cc文件:

    protoc --cpp_out=./ ./echo.proto

    3.client端

    https://blog.csdn.net/huntinux/article/details/81124091

    https://cloud.tencent.com/developer/article/1915035

    3.1 首先通过gflags解析参数:

    GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);

    3.2 client端要包含channel头文件,channel可以认为就是client客户端:

    #include <brpc/channel.h>
    
    //Channel是与Server通信的通道。Channel时thread-safe的,可以被多个线程共享。
    brpc::Channel channel;

    接下来对Channel进行类初始化,用到了上面定义的命令行参数,主要包括:协议类型、连接类型、超时时间、重试次数、服务器地址、负载均衡策略。 

        brpc::ChannelOptions options;
        options.protocol = FLAGS_protocol;//"baidu_std"
        options.connection_type = FLAGS_connection_type;//""
        options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/;//1000
        options.max_retry = FLAGS_max_retry;//3
        //Init重载分为连接单个服务器,和服务集群。
        //连接服务集群:
        //定期从naming_service_url指定的命名服务中获得服务器列表,并通过load_balancer_name指定的负载均衡算法选择出一台机器发送请求。
        if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) {
            LOG(ERROR) << "Fail to initialize channel";
            return -1;
        }

    3.3 将Channel传递给Stub类:

    example::EchoService_Stub stub(&channel);
    //通常并不直接使用Channel,而是把Channel传递给一个stub service,
    //注意stub也可以被多个线程共享。

    3.4 通过Stub的Echo接口发送请求,并接收响应:

            example::EchoRequest request;
            example::EchoResponse response;
            brpc::   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);

    注意传递过去的都是引用参数,函数中会自动修改其值,返回后可读取内容。

    stub存根类是调用了channel的CallMethod函数。

    上述也定义了一个controll对象,那么它的作用和channel的区别?我认为是channel负责发送和接收消息,cntl负责保存request和response等结果?那么客户端的cntl和服务端的cntl有什么区别?

    所以是客户端定义了channel,并且在调用方法时需要定义一个cntl,服务端不需要定义。

    4.server端

    继承EchoService类并实现远程调用方法:

    class EchoServiceImpl : public EchoService {...};

    实现:

        //同步Echo服务
        virtual void Echo(google::protobuf::RpcController* cntl_base,//包含了所有request和response之外的参数集合
                          const EchoRequest* request,
                          EchoResponse* response,
                          google::protobuf::Closure* done) {//这个对象确保在return时自动调用done->Run(),RAII机制
            brpc::ClosureGuard done_guard(done);//Server端的done的逻辑主要是发送response回client
            //done由框架创建,递给服务回调,包含了调用服务回调后的后续动作,包括检查response正确性,序列化,打包,发送等逻辑。
            
            brpc::Controller* cntl =//在brpc中可以静态转为brpc::Controller(前提是代码运行brpc.Server中)
                static_cast<brpc::Controller*>(cntl_base);
    
            LOG(INFO) << "Received request[log_id=" << cntl->log_id() 
                      << "] from " << cntl->remote_side() //获得发送该请求的client地址和端口
                      << " to " << cntl->local_side()//获得server端的地址
                      << ": " << request->message()
                      << " (attached=" << cntl->request_attachment() << ")";
    
            // Fill response.
            response->set_message(request->message());
        }

    4.1 cntl_base参数 

    cntl_base是google::protobuf::RpcController*类型,出自protobuf,是控制信息的基类。使用的时候一般转成brpc的控制信息类型:

    brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);

    4.2 done参数

    brpc::ClosureGuard类是基于RAII机制来调用done->Run(),并且确保它一定能够执行。

        ~ClosureGuard() {
            if (_done) {
                _done->Run();
            }
        }

    接口返回(response返回给客户端?)并不代表这个服务的所有逻辑就结束了

    可以自己控制done->Run()的时机:

    • 当response数据准备好之后就立即手工调用done->Run()来让Server返回response,而不需要等待ClosureGuard析构;
    • 在此之后,还可以做一下其他操作,比如打印NOTICE日志、向消息度队列发送一个消息触发某个异步事件等。这些done->Run()之后的操作都是不占用服务响应耗时的!

    当done->Run()执行过之后,cntl、request、response的数据都会失效。如果你要打日志或者执行其他操作,请把需要的数据提前存储到其他变量中,不能再依赖着三个变量。

    5.server端main函数

    核心:

    1. 创建一个brpc::Server对象(server)
    2. 创建一个Service对象(echo_service_impl)
    3. 把Service对象添加到Server对象(server)中
    4. 启动server
    int main(int argc, char* argv[]) {
        GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);
        brpc::Server server;
        example::EchoServiceImpl echo_service_impl;
        //添加服务到服务器
        if (server.AddService(&echo_service_impl, 
                              brpc::SERVER_DOESNT_OWN_SERVICE) != 0) {
            LOG(ERROR) << "Fail to add service";
            return -1;
        }
         // Start the server.
        brpc::ServerOptions options;
        options.idle_timeout_sec = FLAGS_idle_timeout_s;//客户端200s没动作则断开连接
        if (server.Start(point, &options) != 0) {
            LOG(ERROR) << "Fail to start EchoServer";
            return -1;
        }
      server.RunUntilAskedToQuit();//等待到ctrl+c按下,否则就一直启动服务
      return 0;
      }

    5.1 AddService

    函数声明:

    int Server::AddService(google::protobuf::Service* service,
                           ServiceOwnership ownership);

    第一个参数service是谷歌的protobuf中的类型,不是brpc创造的。

    google::protobuf::Service就是protobuf中service关键字所创建的类型:

    service EchoService {
          rpc Echo(EchoRequest) returns (EchoResponse);
    };

    如上就是声明了一个名为EchoService的Service类型。其中的rpc函数名为:Echo

    ServiceOwnership表示Service的所有权:

    1. SERVER_OWNS_SERVICE:server持有service的所有权。则在server关闭时会delete掉service,然后service一般是栈上的对象,而不是指针,所以一般不用这个,而用下面的。
    2. SERVER_DOESNT_OWN_SERVICE:server不持有service的所有权
  • 相关阅读:
    三范式最简单最易记的解释
    Mysql添加用户错误:ERROR 1364 (HY000): Field 'ssl_cipher' doesn't have a default value解决方法
    mysql体系结构管理
    mysql的简单操作
    flush privileges刷新MySQL的系统权限相关表
    二进制安装mysql
    扩展一台mysql-5.6.40
    mysql5.6.40部署过程
    三剑客-awk
    三剑客-sed
  • 原文地址:https://www.cnblogs.com/BlueBlueSea/p/16559793.html
Copyright © 2020-2023  润新知