如何返回错误
为了简化错误处理,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
会继承直接引发错误的重试状态;如果未指定,则默认视为不可重试。