apache / brpc

brpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".
https://brpc.apache.org
Apache License 2.0
16.04k stars 3.92k forks source link

brpc退出时卡死 #2665

Closed zhangyachen closed 1 week ago

zhangyachen commented 2 weeks ago

Describe the bug (描述bug) 在程序退出时,brpc卡在Join的函数不继续执行。

To Reproduce (复现方法) 程序退出时会调用该方法:

TRITONSERVER_Error*
BRPCServer::Stop()
{
  if (!running_) {
    return TRITONSERVER_ErrorNew(
        TRITONSERVER_ERROR_UNAVAILABLE, "BRPC server is not running.");
  }

  // @TODO 检查brpc server如何shutdown
  std::cout << "ready to stop brpc server" << std::endl;
  brpc_server_->Stop(0);
  std::cout << "ready to join brpc server" << std::endl;
  brpc_server_->Join();
  std::cout << "stop brpc server" << std::endl;

  running_ = false;

  return nullptr;  // success
}

加了三个debug日志,发现卡死在Join上了。

场景是上游有多个client创建多个链接到brpc server。我发现当没有流量到brpc server时,就可以正常退出,打印stop brpc server,但是当上游还有流量到brpc server,我kill brpc server进程时,就会出现卡死现象。

Expected behavior (期望行为) 正常退出。

Versions (各种版本) OS: Ubuntu 20.04.5 LTS (Focal Fossa) Compiler:
brpc: master代码 protobuf: 3.16

Additional context/screenshots (更多上下文/截图)

TousakaRin commented 2 weeks ago

client 也是brpc写的吗,正常流程是你发送kill之后,server拒绝接受新的连接,同时为后续所有的rpc都返回ELOGOFF这个错误,brpc的client在收到这个错误之后会把对应socket摘除。

zhangyachen commented 2 weeks ago

client 也是brpc写的吗,正常流程是你发送kill之后,server拒绝接受新的连接,同时为后续所有的rpc都返回ELOGOFF这个错误,brpc的client在收到这个错误之后会把对应socket摘除。

client是百度的java版brpc,https://github.com/baidu/starlight

TousakaRin commented 2 weeks ago

client是百度的java版brpc,https://github.com/baidu/starlight

java的这个版本不太熟,搜了一下代码只找到一个ELOGOFF的错误码定义,似乎没有对它有特别的处理。可以给他们提issue,或者在client的客户代码里自己处理这个错误(应该是能拿到的,这会儿手里没环境不太方便测试)

zhangyachen commented 2 weeks ago

client是百度的java版brpc,https://github.com/baidu/starlight

java的这个版本不太熟,搜了一下代码只找到一个ELOGOFF的错误码定义,似乎没有对它有特别的处理。可以给他们提issue,或者在client的客户代码里自己处理这个错误(应该是能拿到的,这会儿手里没环境不太方便测试)

假如上游不是brpc,而是grpc框架,也会出现这种情况吗?还是说grpc框架会处理这种ELOGOFF

TousakaRin commented 2 weeks ago

假如上游不是brpc,而是grpc框架,也会出现这种情况吗?还是说grpc框架会处理这种ELOGOFF

如果使用的协议是 grpc,brpc server 会把 ELOGOFF 错误转换为 http2 的 connection_goaway, 一般来说 grpc client 是能处理这种错误的。

zhangyachen commented 2 weeks ago

这个问题解决了,当我把上游换成grpc框架时,还是会出现不能退出的问题,确定是我代码写的有问题。后来从server.md中找到了答案,这里面提到:

如果你的server“退不掉”,很有可能是由于某个检索线程没结束或忘记调用done了。

我的brpc server代码是调用一个异步函数,这个异步函数在程序退出时会返回error。伪代码是:

void InferenceServiceImpl::ModelInfer(google::protobuf::RpcController* cntl_base,
                      const inference::ModelInferRequest* request,
                      inference::ModelInferResponse* response,
                      google::protobuf::Closure* done) {
      brpc::ClosureGuard done_guard(done);
      brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
      TRITONSERVER_Error* err = nullptr;

      //将done保存起来,在回调函数中使用
      State* state = StateNew(done, response, cntl);
      // 回调函数
     err = TRITONSERVER_InferenceRequestSetResponseCallback(
            irequest, allocator_,
            &state->alloc_payload_ /* response_allocator_userp */,
            InferResponseComplete, reinterpret_cast<void*>(state));
      // 异步函数
      if (err == nullptr) {
        err = TRITONSERVER_ServerInferAsync(tritonserver_.get(), irequest, nullptr);
      }
      if (err != nullptr) {
        int brpc_status;
        BrpcStatusUtil::Create(&brpc_status, err);
        std::string error_msg = berror(brpc_status);
        cntl->SetFailed(brpc_status, "%s", error_msg.c_str());
        TRITONSERVER_ErrorDelete(err);

        // 新添加的
        return;
      }

      done_guard.release();
}

当异步函数TRITONSERVER_ServerInferAsync报错时,没有return导致brpc::ClosureGuard没有调用done->Run();