RPC 系列(二十八):brpc 错误码

错误码与 Controller

brpc 使用 brpc::Controller 设置和获取一次 RPC 的参数,Controller::ErrorCode() 和 Controller::ErrorText() 则分别是该次 RPC 的错误码和错误描述,RPC 结束后才能访问,否则结果未定义。ErrorText() 由 Controller 的基类 google::protobuf::RpcController 定义,ErrorCode() 则是 brpc::Controller 定义的。Controller 还有个 Failed() 方法告知该次 RPC 是否失败。

brpc 的 client 端和 server 端都有 Controller,都可以通过 SetFailed() 修改其中的 ErrorCode 和 ErrorText。当多次调用一个 Controller 的 SetFailed 时,ErrorCode 会被覆盖,ErrorText 则是添加而不是覆盖。在 client 端,框架会额外加上第几次重试,在 server 端,框架会额外加上 server 的地址信息。

client 端 Controller 的 SetFailed() 常由框架调用,比如发送 request 失败,接收到的 response 不符合要求等等。只有在进行较复杂的访问操作时用户才可能需要设置 client 端的错误,比如在访问后端前做额外的请求检查,发现有错误时把 RPC 设置为失败。

server 端 Controller 的 SetFailed() 常由用户在服务回调中调用。当处理过程发生错误时,一般调用 SetFailed() 并释放资源后就 return 了。框架会把错误码和错误信息按交互协议填入 response,client 端的框架收到后会填入它那边的 Controller 中,从而让用户在 RPC 结束后取到。

注意:server 端在 SetFailed() 时默认不打印送往 client 的错误。打日志是比较慢的,在繁忙的线上磁盘上,很容易出现巨大的 lag。一个错误频发的 client 容易减慢整个 server 的速度而影响到其他的 client,理论上来说这甚至能成为一种攻击手段。对于希望在 server 端看到错误信息的场景,可以打开 gflag -log_error_text(可动态开关),server 会在每次失败的 RPC 后把对应 Controller 的 ErrorText() 打印出来。

错误码的来源

brpc 使用的所有 ErrorCode 都定义在 errno.proto 中,SYS_ 开头的来自 linux 系统,与 /usr/include/errno.h 中定义的精确一致,定义在 proto 中是为了跨语言。其余的是 brpc 自有的。

berror(error_code) 可获得 error_code 的描述,berror() 可获得当前 system errno 的描述。ErrorText() != berror(ErrorCode()),ErrorText() 会包含更具体的错误信息。brpc 默认包含 berror,你可以直接使用。

常见错误码

brpc 中常见错误的打印内容列表如下:

错误码 数值 重试 说明 日志
EAGAIN 11 是 同时发送的请求过多。软限,很少出现。 Resource temporarily unavailable
ETIMEDOUT 110 是 连接超时。 Connection timed out
EHOSTDOWN 112 是 找不到可用的server。server可能停止服务了,也可能正在退出中(返回了ELOGOFF)。 “Fail to select server from …” “Not connected to … yet”
ENOSERVICE 1001 否 找不到服务,不太出现,一般会返回ENOMETHOD。
ENOMETHOD 1002 否 找不到方法。 形式广泛,常见如 “Fail to find method=…”
EREQUEST 1003 否 request序列化错误,client端和server端都可能设置 形式广泛:”Missing required fields in request: …” “Fail to parse request message, …” “Bad request”
EAUTH 1004 否 认证失败 “Authentication failed”
ETOOMANYFAILS 1005 否 ParallelChannel中太多子channel失败 “%d/%d channels failed, fail_limit=%d”
EBACKUPREQUEST 1007 是 触发backup request时设置,不会出现在ErrorCode中,但可在/rpcz里看到 “reached backup timeout=%dms”
ERPCTIMEDOUT 1008 否 RPC超时 “reached timeout=%dms”
EFAILEDSOCKET 1009 是 RPC进行过程中TCP连接出现问题 “The socket was SetFailed”
EHTTP 1010 否 非2xx状态码的HTTP访问结果均认为失败并被设置为这个错误码。默认不重试,可通过RetryPolicy定制 Bad http call
EOVERCROWDED 1011 是 连接上有过多的未发送数据,常由同时发起了过多的异步访问导致。可通过参数-socket_max_unwritten_bytes控制,默认8MB。 The server is overcrowded
EINTERNAL 2001 否 Server端Controller.SetFailed没有指定错误码时使用的默认错误码。 “Internal Server Error”
ERESPONSE 2002 否 response解析错误,client端和server端都可能设置 形式广泛:”Missing required fields in response: …” “Fail to parse response message, …” “Bad response”
ELOGOFF 2003 是 Server已经被Stop了 “Server is going to quit”
ELIMIT 2004 是 同时处理的请求数超过ServerOptions.max_concurrency了 “Reached server’s limit=%d on concurrent requests”

自定义错误码

在 C++/C 中你可以通过宏、常量、enum 等方式定义 ErrorCode:

#define ESTOP -114                // C/C++
static const int EMYERROR = 30;   // C/C++
const int EMYERROR2 = -31;        // C++ only

如果你需要用 berror 返回这些新错误码的描述,你可以在 .cpp 或 .c 文件的全局域中调用 BAIDU_REGISTER_ERRNO(error_code, description) 进行注册,比如:

BAIDU_REGISTER_ERRNO(ESTOP, "the thread is stopping")
BAIDU_REGISTER_ERRNO(EMYERROR, "my error")

strerror 和 strerror_r 不认识使用 BAIDU_REGISTER_ERRNO 定义的错误码,自然地,printf 类的函数中的 %m 也不能转化为对应的描述,你必须使用 %s 并配以 berror()。

errno = ESTOP;
printf("Describe errno: %m\n");                              // [Wrong] Describe errno: Unknown error -114
printf("Describe errno: %s\n", strerror_r(errno, NULL, 0));  // [Wrong] Describe errno: Unknown error -114
printf("Describe errno: %s\n", berror());                    // [Correct] Describe errno: the thread is stopping
printf("Describe errno: %s\n", berror(errno));               // [Correct] Describe errno: the thread is stopping

当同一个 error code 被重复注册时,那么会出现链接错误:

redefinition of `class BaiduErrnoHelper<30>'

或者在程序启动时会 abort:

Fail to define EMYERROR(30) which is already defined as `Read-only file system', abort

你得确保不同的模块对 ErrorCode 的理解是相同的,否则当两个模块把一个错误码理解为不同的错误时,它们之间的交互将出现无法预计的行为。为了防止这种情况出现,你最好这么做:

  • 优先使用系统错误码,它们的值和含义一般是固定不变的。
  • 多个交互的模块使用同一份错误码定义,防止后续修改时产生不一致。
  • 使用 BAIDU_REGISTER_ERRNO 描述新错误码,以确保同一个进程内错误码是互斥的。

系列导航


   转载规则


《RPC 系列(二十八):brpc 错误码》 由 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
RPC 系列(二十九):brpc 的 gflags 配置 RPC 系列(二十九):brpc 的 gflags 配置
brpc 使用 gflags 管理配置。如果你的程序也使用 gflags,那么你应该已经可以修改和 brpc 相关的 flags,你可以浏览 flags 服务了解每个 flag 的具体功能。如果你的程序还没有使用 gflags,我们建议你使
2024-03-19
下一篇 
RPC 系列(二十七):brpc 自定义协议 RPC 系列(二十七):brpc 自定义协议
server 端多协议brpc server 一个端口支持多种协议,大部分时候这对部署和运维更加方便。由于不同协议的格式大相径庭,严格地来说,一个端口很难无二义地支持所有协议。出于解耦和可扩展性的考虑,也不太可能集中式地构建一个针对所有协议
2024-03-05
  目录