使用brpc遇到的一些bug和解决方案

这篇文章记录一下在使用brpc的时候遇到的bug以及解决方案,长期更新。

流式RPC遇到"pure virtual method called"错误

流式RPC的使用需要继承brpc::StreamInputHandler,并且实现它的三个纯虚函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class StreamReciver : public brpc::StreamInputHandler {
public:
int on_received_messages(brpc::StreamId id,
butil::IOBuf *const messages[],
size_t size) {
// 当接收到消息时,调用该方法处理消息
}
void on_idle_timeout(brpc::StreamId id) {
// 当超时未接收到消息时,调用该方法处理
}
void on_closed(brpc::StreamId id) {
// 关闭stream以后,调用该方法处理
}
}

在有些应用场景中,StreamReceiver可能并不长,在完成一次流式传输以后就立刻析构了,代码结构类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 流式传输前 */

{
brpc::StreamId sd;
brpc::StreamOptions stream_options;
StreamReciver stream_recevier; // StreamReceiver实例化
stream_options.handler = &stream_recevier;
if(brpc::StreamCreate(&sd, cntl, &stream_options) != 0) {
... ... // 错误处理
}
stub->StreamMethod(&cntl, &request, &response, NULL);

...... // 数据传输

brpc::StreamClose(sd); // 传输结束,关闭stream
}

/*流式传输后*/

这样的代码结构里,stream_recevier的生命周期仅限于中括号内部,一旦运行退出中括号,它就被析构了。但是前面有说到StreamReciver::on_closed方法会在stream被关闭以后调用,而brpc::StreamClose(sd)不会等到StreamReciver::on_close被调用才返回。因此,有可能出现stream_recevier先被析构,而后on_close被调用的情况,此时虚函数表找到的就是brpc::StreamInputHandler这个基类的on_close了,是一个纯虚函数,从而引发了"pure virtual method called"的错误。

解决方案

首先,理所当然的解决方案当然是避免这种代码结构,可以让StreamReciver作为brpc Service的成员变量,生命周期和服务一致。

但是,有时候不可避免地会出现这种代码结构,例如不同的情况下需要构造不同的StreamReciver,在准备建立stream时才构造,完成传输后立刻销毁。我对于这种情况的对策是给StreamReciver加一个close_的成员变量,并在on_close方法内将其置为true。关闭stream以后,轮询close_,判断on_close方法是否已经被调用,确认被调用后再退出。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
class StreamReciver : public brpc::StreamInputHandler {
public:
int on_received_messages(brpc::StreamId id,
butil::IOBuf *const messages[],
size_t size) {}
void on_idle_timeout(brpc::StreamId id) {}
void on_closed(brpc::StreamId id) { close_ = true; }
bool is_close() { return close_; }

private:
bool close_ = false;
}
1
2
3
4
5
6
7
8
9
10
11
/* 流式传输前 */

{
......
brpc::StreamClose(sd); // 关闭stream
while(!stream_recevier) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

/*流式传输后*/

这样就可以确保on_close方法在stream_recevier被析构前得到调用,避免纯虚函数调用错误。

流式RPC遇到"Fail to parse response from xxxx:xxxx by streaming_rpc at client-side"错误

这个问题在brpc的issue#392有被讨论到,具体原因不明,可能是普通RPC和流式RPC混用同一个连接导致的。

解决方案

目前的解决方案是客户端创建brpc::Channel时,将brpc::ChannelOptionsconnection_type设置为"pooled",如下:

1
2
3
4
5
6
brpc::Channel channel;
brpc::ChannelOptions options;
options.connection_type = "pooled";
if(channel.Init(server_ep, &options) != 0) {
// 错误处理
}

按照这样修改代码后,暂时没遇到错误了,但是不确保是不是真的解决了问题。


使用brpc遇到的一些bug和解决方案
http://ztorchan.com/2023/06/02/some-brpc-bugs-and-solutions/
作者
ztorchan
发布于
2023年6月2日
许可协议