通过echo_server带你入门brpc!

简介: 前文我们介绍过如何编译安装brpc(brpc最新安装上手指南),今天通过echo_server来介绍一下brpc的基本使用方法与细节。

前文我们介绍过如何编译安装brpc(brpc最新安装上手指南),今天通过echo_server来介绍一下brpc的基本使用方法与细节。


main函数


int main(int argc, char* argv[]) {
    // 解析gflag
    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;
    }
    brpc::ServerOptions options;
    options.idle_timeout_sec = FLAGS_idle_timeout_s;
    if (server.Start(FLAGS_port, &options) != 0) {
        LOG(ERROR) << "Fail to start EchoServer";
        return -1;
    }
    server.RunUntilAskedToQuit();
    return 0;
}


可以忽略gflag相关的代码,这个main函数的核心就是:


  1. 创建一个brpc::Server对象(server)


  1. 创建一个Service对象(echo_service_impl)


  1. 把Service对象添加到Server对象(server)中


  1. 启动server


一个Service对象就是一个服务,可以理解为使用了Brpc的Server对外提供的一个接口。一个Server可以Add多态Service,也就是提供多个对外接口。


AddService


Service


简单讲一个下AddService:


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


参数一service是google::protobuf::Service*类型,很明显这不是brpc发明的类型,而是谷歌protobuf的类型。因为brpc的baidu_std协议也是重度依赖protobuf的。


所谓的google::protobuf::Service 其实就是protobuf中service关键字所创建的类型,我们平时使用pb的时候,可能message关键字用的比较多。


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


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


ServiceOwnership


参数二,表示的是Service的所有权。枚举值有二:


  1. SERVER_OWNS_SERVICE:server持有service的所有权


  1. SERVER_DOESNT_OWN_SERVICE:server不持有service的所有权


不过所有权一词还是不够直观,完全没办法顾名思义。这里我可以直接告诉这二者的各种差异:


在AddService内部,有各种各样的原因可能导致Add失败,彼时这个所有权宣示着Server使用应该对Service的指针进行delete操作,从而避免内存泄漏。显然,如上面例子所示,栈上创建的Service对象是不应该执行delete操作的。故而用SERVER_DOESNT_OWN_SERVICE来避免执行delete。


而使用SERVER_OWNS_SERVICE的示例如下:


auto _echo_service = new EchoServiceImpl;
if (_server.AddService(_echo_service,
                       brpc::SERVER_OWNS_SERVICE) != 0) {
    LOG(FATAL) << "Fail to add service";
}


通常不需要这样。好了,不太要紧的讲完了,下面讲点要紧的。


实现Service的逻辑


前文讲过:


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


如上就是声明了一个名为EchoService的Service类型。我们要做的就是继承EchoService这一父类,然后从中实现rpc函数:Echo。在Echo函数中写我们自己的逻辑,表示的就是收到请求之后的处理逻辑。将官方例子精简如下:


class EchoServiceImpl : public EchoService {
public:
    EchoServiceImpl() {};
    virtual ~EchoServiceImpl() {};
    virtual void Echo(google::protobuf::RpcController* cntl_base,
                      const EchoRequest* request,
                      EchoResponse* response,
                      google::protobuf::Closure* done) {
        brpc::ClosureGuard done_guard(done);
        brpc::Controller* cntl =
            static_cast<brpc::Controller*>(cntl_base);
        response->set_message(request->message());
    }
};


Echo函数的4个参数是固定的,任何Service都如此。


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


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


request 和 response就是表示请求和返回的对象指针了,是protobuf的message类型,类型定义在proto中:


message EchoRequest {
      required string message = 1;
};
message EchoResponse {
      required string message = 1;
};

这个是可以由我们自己根据实际需要来自定义请求字段与返回字段。


done很重要


最后一个参数done其实也是比较重要的。当这里done->Run()被调用的时候,服务就立即给客户端返回response了。如果忘记了调用,那么这个服务会卡住。因此brpc提供了一个基于RAII思想的保护器brpc::ClosureGuard来确保done的Run()函数一定能被调用。


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


值得一提的是,接口返回并不代表这个服务的所有逻辑就结束了,其实你可以自己控制done->Run()的时机,比如当response数据准备好之后就立即手工调用done->Run()来让Server返回response,而不需要等待ClosureGuard析构。在此之后,其实你可以还可以做一下其他操作,比如打印NOTICE日志、向消息度队列发送一个消息触发某个异步事件等。这些done->Run()之后的操作都是不占用服务响应耗时的!


之前大家用过的一些RPC框架可能打印NOTICE日志等逻辑都是在response之前做的,response是所有自定义的逻辑全部执行完之后才调用。brpc则不同,了解到这点很重要


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


相关文章
Web server failed to start. Port XXX was already in use.【完美解决方案】
Web server failed to start. Port XXX was already in use.【完美解决方案】
Web server failed to start. Port XXX was already in use.【完美解决方案】
Web server failed to start. Port XXX was already in use.原因分析-解决方案
Web server failed to start. Port XXX was already in use.原因分析-解决方案
479 1
Web server failed to start. Port XXX was already in use.原因分析-解决方案
|
5月前
|
关系型数据库 MySQL 数据库
【已解决】[图文步骤] message from server: “Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server“
【已解决】[图文步骤] message from server: “Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server“
214 0
|
6月前
|
存储 网络协议 Java
编写UDP版本的客户-服务器程序(echo server 和 echo client)
编写UDP版本的客户-服务器程序(echo server 和 echo client)
84 0
|
应用服务中间件 Android开发
Server Tomcat v9.0 Server at localhost failed to start问题的解决
Server Tomcat v9.0 Server at localhost failed to start问题的解决
431 0
|
6月前
|
关系型数据库 MySQL 数据库
Host 'XXX' is not allowed to connect to this MySQL server 解决方案
Host 'XXX' is not allowed to connect to this MySQL server 解决方案
|
12月前
|
关系型数据库 MySQL Linux
DVWA CentOS Could not connect to the MySQL service. Please check the config file.
DVWA CentOS Could not connect to the MySQL service. Please check the config file.
66 0
|
应用服务中间件 Android开发
“Server Tomcat v9.0 Server at localhost was unable to start within 45 seconds“的解决方案
“Server Tomcat v9.0 Server at localhost was unable to start within 45 seconds“的解决方案
775 0
“Server Tomcat v9.0 Server at localhost was unable to start within 45 seconds“的解决方案
|
Python
python报错——check_hostname requires server_hostname
python报错——check_hostname requires server_hostname
305 0
python报错——check_hostname requires server_hostname
|
应用服务中间件 数据安全/隐私保护 nginx
Caddy Server使用进阶
Caddy Server使用进阶
833 0