如何返回错误
为了简化错误处理,pingora-error crate 导出了一个自定义的 Result 类型,广泛用于其他 Pingora crates。
Result 的错误变体中的 Error 结构体是对任意错误类型的封装。它允许用户标记底层错误的来源,并附加其他自定义的上下文信息。
用户通常需要通过传播已有错误或创建全新的错误来返回错误。pingora-error 提供了便捷的错误构建函数来实现这一点。
示例
例如,当某个预期的请求头不存在时,可以返回一个错误:
fn validate_req_header(req: &RequestHeader) -> Result<()> {// 验证 `host` 请求头是否存在req.headers().get(http::header::HOST).ok_or_else(|| Error::explain(InvalidHTTPHeader, "未检测到 host 请求头"))}impl MyServer {pub async fn handle_request_filter(&self,http_session: &mut Session,ctx: &mut CTX,) -> Result<bool> {validate_req_header(session.req_header()?).or_err(HTTPStatus(400), "缺少必要的请求头")?;Ok(true)}}
如果未找到 host 请求头,validate_req_header 会返回一个使用 Error::explain 创建的新 Error,同时附带相关类型(如 InvalidHTTPHeader)和可记录在错误日志中的上下文信息。
这个错误最终会传播到请求过滤器,并通过 or_err 返回一个新的 HTTPStatus 错误。作为默认的 pingora-proxy 的 fail_to_proxy() 阶段的一部分,此错误不仅会被记录,还会向下游发送一个 400 Bad Request 的响应。
请注意,原始引发的错误也会在错误日志中可见。or_err 会将原始错误封装在一个新的错误中,并添加上下文信息,而 Error 的 Display 实现会打印导致错误的整个链条。
指南
一个错误通常包含以下部分:
- 类型(如
ConnectionClosed) - 来源(如
Upstream、Downstream、Internal) - 原因(可选,被封装的另一个错误)
- 上下文(可选,用户提供的任意字符串描述)
可以使用如 new_in / new_up / new_down 之类的函数创建最小错误,每个函数都指定一个来源并要求用户提供错误类型。
通常:
- 如果需要创建一个没有直接原因但带有更多上下文的新错误,使用
Error::explain。也可以使用explain_err替换Result中的潜在错误为新错误。 - 如果需要将引发的错误封装到一个带有更多上下文的新错误中,使用
Error::because。也可以使用or_err将Result中的潜在错误替换为包含原始错误的新错误。
重试
错误可以是“可重试的”。如果错误是可重试的,pingora-proxy 将允许对上游请求进行重试。一些错误仅在重用连接时可重试,例如处理远端断开尝试重用的连接的情况。
默认情况下,新创建的 Error 会继承直接引发错误的重试状态;如果未指定,则默认视为不可重试。
