2021SC@SDUSC BRPC源码分析(三) Channel

2021SC@SDUSC BRPC源码分析(三) Channel

目录

  • 2021SC@SDUSC BRPC源码分析(三) Channel
  • 背景知识
    • 同步和异步
        • 同步
        • 异步
  • 代码分析
    • 同步访问
    • 异步访问
      • 继承google::protobuf::Closure

背景知识

同步和异步

同步和异步关注的是消息通信机制。

同步

同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。
2021SC@SDUSC BRPC源码分析(三) Channel_第1张图片
同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

异步

异步,和同步相反 调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用。
2021SC@SDUSC BRPC源码分析(三) Channel_第2张图片
异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

代码分析

同步访问

CallMethod会阻塞到收到server端返回response或发生错误(包括超时)。

同步访问中的response/controller不会在CallMethod后被框架使用,它们都可以分配在栈上。注意,如果request/response字段特别多字节数特别大的话,还是更适合分配在堆上。

MyRequest request;
MyResponse response;
brpc::Controller cntl;
XXX_Stub stub(&channel);
 
request.set_foo();
cntl.set_timeout_ms();
stub.some_method(&cntl, &request, &response, NULL);
if (cntl->Failed()) {
     
    // RPC失败了. response里的值是未定义的,勿用。
} else {
     
    // RPC成功了,response里有我们想要的回复数据。
}

异步访问

给CallMethod传递一个额外的回调对象done,CallMethod在发出request后就结束了,而不是在RPC结束后。当server端返回response或发生错误(包括超时)时,done->Run()会被调用。对RPC的后续处理应该写在done->Run()里,而不是CallMethod后。

由于CallMethod结束不意味着RPC结束,response/controller仍可能被框架及done->Run()使用,它们一般得创建在堆上,并在done->Run()中删除。如果提前删除了它们,那当done->Run()被调用时,将访问到无效内存。

发起异步请求后Request和Channel也可以立刻析构。这两样和response/controller是不同的。注意:这是说Channel的析构可以立刻发生在CallMethod之后,并不是说析构可以和CallMethod同时发生,删除正被另一个线程使用的Channel是未定义行为(很可能crash)。

static void OnRPCDone(MyResponse* response, brpc::Controller* cntl) {
     
    // unique_ptr会帮助我们在return时自动删掉response/cntl,防止忘记。gcc 3.4下的unique_ptr是模拟版本。
    std::unique_ptr<MyResponse> response_guard(response);
    std::unique_ptr<brpc::Controller> cntl_guard(cntl);
    if (cntl->Failed()) {
     
        // RPC失败了. response里的值是未定义的,勿用。
    } else {
     
        // RPC成功了,response里有想要的数据。开始RPC的后续处理。    
    }
    // NewCallback产生的Closure会在Run结束后删除自己。
}
 
MyResponse* response = new MyResponse;
brpc::Controller* cntl = new brpc::Controller;
MyService_Stub stub(&channel);
 
MyRequest request;  
request.set_foo();
cntl->set_timeout_ms();
stub.some_method(cntl, &request, response, google::protobuf::NewCallback(OnRPCDone, response, cntl));

继承google::protobuf::Closure

使用NewCallback的缺点是要分配三次内存:response, controller, done。如果profiler证明这儿的内存分配有瓶颈,可以考虑自己继承Closure,把response/controller作为成员变量,这样可以把三次new合并为一次。但缺点就是代码不够美观,如果内存分配不是瓶颈,别用这种方法。

class OnRPCDone: public google::protobuf::Closure {
     
public:
    void Run() {
     
        // unique_ptr会帮助我们在return时自动delete this,防止忘记。gcc 3.4下的unique_ptr是模拟版本。
        std::unique_ptr<OnRPCDone> self_guard(this);
          
        if (cntl->Failed()) {
     
            // RPC失败了. response里的值是未定义的,勿用。
        } else {
     
            // RPC成功了,response里有想要的数据。开始RPC的后续处理。
        }
    }
 
    MyResponse response;
    brpc::Controller cntl;
}
 
OnRPCDone* done = new OnRPCDone;
MyService_Stub stub(&channel);
 
MyRequest request;  // 不用new request,即使在异步访问中.
request.set_foo();
done->cntl.set_timeout_ms();
stub.some_method(&done->cntl, &request, &done->response, done);

你可能感兴趣的:(c++)