处理失败和故障切换
Pingora-proxy 允许用户定义如何在代理请求的整个生命周期中处理失败。
当响应头尚未发送到下游时,用户有以下选项:
- 发送错误页面到下游,然后终止请求。
- 重试同一个上游请求。
- 如果适用,尝试另一个上游请求。
如果响应头已经发送到下游,代理无法做任何处理,只能记录错误并放弃请求。
重试 / 故障切换
为了实现重试或故障切换,fail_to_connect() 或 error_while_proxy() 需要将错误标记为“可重试”。对于故障切换,还需要在 CTX 中更新状态,指示 upstream_peer() 不要再使用同一个 Peer。
安全性
通常情况下,幂等的 HTTP 请求(例如 GET)是安全可重试的。而非幂等请求(例如 POST)如果已发送,则不安全可重试。当调用 fail_to_connect() 时,Pingora-proxy 保证未向上游发送任何内容。在调用 error_while_proxy() 后,不建议重试非幂等请求,除非对上游服务器的行为非常了解并确定其安全。
示例
在以下示例中,我们通过 CTX 的 tries 变量跟踪连接尝试次数。在 upstream_peer 方法中,当 tries < 1 时,我们连接到 192.0.2.1。如果连接失败,在 fail_to_connect 中递增 tries 并调用 e.set_retry(true),标记该错误为可重试。当再次重试时,进入 upstream_peer 方法,这次连接到 1.1.1.1。如果仍然无法连接,我们返回 502,因为仅在 tries = 0 时设置了 e.set_retry(true)。
pub struct MyProxy();pub struct MyCtx {tries: usize,}#[async_trait]impl ProxyHttp for MyProxy {type CTX = MyCtx;fn new_ctx(&self) -> Self::CTX {MyCtx { tries: 0 }}fn fail_to_connect(&self,_session: &mut Session,_peer: &HttpPeer,ctx: &mut Self::CTX,mut e: Box<Error>,) -> Box<Error> {if ctx.tries > 0 {return e;}ctx.tries += 1;e.set_retry(true); // 标记为可重试e}async fn upstream_peer(&self,_session: &mut Session,ctx: &mut Self::CTX,) -> Result<Box<HttpPeer>> {let addr = if ctx.tries < 1 {("192.0.2.1", 443) // 第一次尝试的上游} else {("1.1.1.1", 443) // 故障切换后的上游};let mut peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string()));peer.options.connection_timeout = Some(Duration::from_millis(100)); // 设置超时时间Ok(peer)}}
示例解释
- 上下文管理:
MyCtx保存了连接尝试次数 (tries)。 - 故障处理:
fail_to_connect判断是否需要重试,并标记错误为可重试。 - 上游选择:
upstream_peer根据tries决定连接哪个上游。 - 超时设置:通过
HttpPeer的connection_timeout配置连接超时。
这种方式确保了简单而有效的重试和故障切换策略,同时避免了非安全的重试行为。
