错误码
错误码
了解 bRPC 客户端的错误码。
brpc 使用 brpc::Controller 来为一次 RPC 设置和获取参数。Controller::ErrorCode() 和 Controller::ErrorText() 分别返回该 RPC 的错误码和错误描述,仅在 RPC 完成后才能访问,否则结果未定义。ErrorText() 由 Controller 的基类定义:google::protobuf::RpcController,而 ErrorCode() 由 brpc::Controller 定义。Controller 还提供了一个方法 Failed(),用于判断 RPC 是否失败。这三个方法之间的关系:
- 当
Failed()为 true 时,ErrorCode()必须非零,且ErrorText()必须非空。 - 当
Failed()为 false 时,ErrorCode()为 0,且ErrorText()未定义(目前在 brpc 中为空,但你最好不要依赖这一点)。
将 RPC 标记为失败
brpc 中的客户端和服务端都拥有 Controller,可以通过 setFailed() 来设置,以修改 ErrorCode 和 ErrorText。多次调用 Controller::SetFailed 会保留最后一个 ErrorCode,而 拼接 各次的 ErrorText,而不是只保留最后一个。框架会为 ErrorText 添加额外前缀来完善它:客户端侧为重试次数,服务端侧为服务器地址。
客户端侧的 Controller::SetFailed() 通常由框架调用,例如发送失败、响应不完整等情况。在某些情况下也可能在客户端侧设置错误。例如,如果发送请求前的额外校验失败,你可以为该 RPC 设置错误。
服务端侧的 Controller::SetFailed() 通常由用户在服务回调中调用。一般来说,当出现错误时,用户会调用 SetFailed(),释放所有资源并从回调中返回。框架会根据通信协议把错误码和错误消息填充到响应中。当响应被收到时,其中的错误会被设置到客户端侧的 Controller 中,以便用户在 RPC 结束后获取它们。请注意,服务端默认不会向客户端打印错误,因为频繁的日志输出可能因繁重的磁盘 IO 而显著影响服务器性能。一个疯狂产生错误的客户端会拖慢整个服务器,影响所有其他客户端,甚至可能成为针对服务器的攻击手段。如果你确实希望在服务器上查看错误信息,可以开启 gflag -log_error_text(可在运行时修改),服务器会记录每个失败 RPC 对应 Controller 的 ErrorText。
brpc 中的错误码
brpc 中的所有错误码都定义在 errno.proto 中,其中以 SYS_ 开头的错误码由 Linux 系统定义,与 /usr/include/errno.h 中定义的完全相同。我们将其放在 .proto 中是为了跨语言。其余错误码由 brpc 定义。
berror(error_code) 获取错误码的描述,berror() 获取当前 system errno 的描述。请注意 ErrorText() != berror(ErorCode()),因为 ErrorText() 包含更具体的信息。brpc 默认包含 berror,因此你可以直接在项目中使用它。
下表列出了常见错误码及其描述:
| 错误码 | 值 | 是否重试 | 描述 | 日志消息 |
|
|----------------|-------|-------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| EAGAIN | 11 | 是 | 同时请求过多,因其为软限制,极少发生。 | Resource temporarily unavailable |
| ENODATA | 61 | 是 | 1. Naming Service 返回的服务器列表为空。2. 当 Naming Service 发生变更且所有实例都被修改时,Naming Service 会先执行 Remove all 再执行 Add all 来更新 LB,在此期间 LB 的实例列表可能在短时间内变为空。 | Fail to select server from xxx |
| ETIMEDOUT | 110 | 是 | 连接超时。 | Connection timed out |
| EHOSTDOWN | 112 | 是 | 可能的原因:A. Naming Server 返回的列表不为空,但 LB 无法选择可用服务器,于是 LB 返回 EHOSTDOWN 错误。具体可能原因:a. 服务器正在退出(返回 ELOGOFF)b. 服务器因之前的某个故障而被阻塞,阻塞的具体逻辑:1. 对于单连接类型,唯一的连接 socket 被 SetFail 阻塞,代码中有多处调用 SetFailed 会触发该阻塞。2. 对于池化/短连接类型,只有当错误码满足 does_error_affect_main_socket(ECONNREFUSED、ENETUNREACH、EHOSTUNREACH 或 EINVAL)时才会被阻塞 3. 阻塞后,会有一个 CheckHealth 线程执行健康检查,仅尝试连接,检查间隔由 SocketOptions 的 health_check_interval_s 控制,连接成功后 Socket 将解除阻塞。B. 使用 SingleServer 方法初始化 Channel(不使用 LB),且唯一的连接处于 LOGOFF 或被阻塞状态(同上) | “Fail to select server from …” “Not connected to … yet” |
| ENOSERVICE | 1001 | 否 | 无法定位服务,极少发生,通常表现为 ENOMETHOD | |
| ENOMETHOD | 1002 | 否 | 无法定位方法。 | Misc forms, common ones are “Fail to find method=…” |
| EREQUEST | 1003 | 否 | 请求序列化失败,可能在客户端或服务端被设置 | Misc forms: “Missing required fields in request: …” “Fail to parse request message, …” “Bad request” |
| EAUTH | 1004 | 否 | 认证失败 | “Authentication failed” |
| ETOOMANYFAILS | 1005 | 否 | ParallelChannel 内部子通道失败过多 | “%d/%d channels failed, fail_limit=%d” |
| EBACKUPREQUEST | 1007 | 是 | 触发备份请求时被设置。不会由 ErrorCode() 直接返回,可在 /rpcz 的 span 中查看 | “reached backup timeout=%dms” |
| ERPCTIMEDOUT | 1008 | 否 | RPC 超时。 | “reached timeout=%dms” |
| EFAILEDSOCKET | 1009 | 是 | RPC 过程中连接断开 | “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 | 否 | Controller::SetFailed 未指定错误码时的默认错误。 | Internal Server Error |
| ERESPONSE | 2002 | 否 | 响应序列化失败,可能在客户端或服务端被设置 | Misc forms: “Missing required fields in response: …” “Fail to parse response message, " “Bad response” |
| ELOGOFF | 2003 | 是 | 服务器已停止 | “Server is going to quit” |
| ELIMIT | 2004 | 是 | 正在并发处理的请求数量超过 ServerOptions.max_concurrency | “Reached server’s limit=%d on concurrent requests” |
用户自定义错误码
在 C/C++ 中,错误码可以通过宏、常量或枚举来定义:
#define ESTOP -114 // C/C++
static const int EMYERROR = 30; // C/C++
const int EMYERROR2 = -31; // C++ only如果需要通过 berror 获取错误描述,请通过 BAIDU_REGISTER_ERRNO(error_code, description) 在 C/CPP 文件的全局作用域中注册它,例如:
BAIDU_REGISTER_ERRNO(ESTOP, "the thread is stopping")
BAIDU_REGISTER_ERRNO(EMYERROR, "my error")请注意,strerror 和 strerror_r 不会识别 BAIDU_REGISTER_ERRNO 定义的错误码,printf 中使用的 %m 也不会。你必须使用与 berror 配对的 %s:
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当错误码的注册发生重复时,如果该错误码是在 C++ 中定义的,则会产生链接错误:
redefinition of `class BaiduErrnoHelper<30>'或者程序在启动前中止:
Fail to define EMYERROR(30) which is already defined as `Read-only file system', abort你必须确保不同的模块对同一个 ErrorCode 有一致的理解。否则,两个对错误码解释不同的模块之间的交互行为可能是未定义的。为了防止这种情况发生,建议你遵循以下原则:
- 优先使用具有固定取值和含义的系统错误码,通常而言它们是通用的。
- 在多个模块之间共享错误码的定义代码,以防止修改之后出现不一致。
- 使用
BAIDU_REGISTER_ERRNO来描述新增错误码,从而确保在同一个进程内,同一个错误码只被定义一次。
最后修改于 2022 年 1 月 9 日:[基于 hugo 的新版 brpc 网站(94b25d711)]](https://github.com/apache/brpc-website/commit/94b25d7110944f3d4b2071b5188c691b23ffe3a9)
评论
登录后参与评论
KnowForge