brpc退出时卡死
zhangyachen opened this issue · comments
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 (更多上下文/截图)
client 也是brpc写的吗,正常流程是你发送kill之后,server拒绝接受新的连接,同时为后续所有的rpc都返回ELOGOFF这个错误,brpc的client在收到这个错误之后会把对应socket摘除。
client 也是brpc写的吗,正常流程是你发送kill之后,server拒绝接受新的连接,同时为后续所有的rpc都返回ELOGOFF这个错误,brpc的client在收到这个错误之后会把对应socket摘除。
client是百度的java版brpc,https://github.com/baidu/starlight
client是百度的java版brpc,https://github.com/baidu/starlight
java的这个版本不太熟,搜了一下代码只找到一个ELOGOFF的错误码定义,似乎没有对它有特别的处理。可以给他们提issue,或者在client的客户代码里自己处理这个错误(应该是能拿到的,这会儿手里没环境不太方便测试)
client是百度的java版brpc,https://github.com/baidu/starlight
java的这个版本不太熟,搜了一下代码只找到一个ELOGOFF的错误码定义,似乎没有对它有特别的处理。可以给他们提issue,或者在client的客户代码里自己处理这个错误(应该是能拿到的,这会儿手里没环境不太方便测试)
假如上游不是brpc,而是grpc框架,也会出现这种情况吗?还是说grpc框架会处理这种ELOGOFF
假如上游不是brpc,而是grpc框架,也会出现这种情况吗?还是说grpc框架会处理这种ELOGOFF
如果使用的协议是 grpc,brpc server 会把 ELOGOFF 错误转换为 http2 的 connection_goaway, 一般来说 grpc client 是能处理这种错误的。
这个问题解决了,当我把上游换成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();