订阅以接收新文章的通知:

开源 Pingora ——我们用于构建可编程网络服务的 Rust 框架

2024-02-28

5 分钟阅读时间
这篇博文也有 EnglishFrançaisDeutsch日本語한국어PortuguêsEspañol繁體中文版本。

今天,我们隆重宣布开源 Pingora——我们一直用来构建网络服务的 Rust 框架,这些服务驱动着 Cloudflare 上的大部分流量。Pingora 是根据 Apache 许可证 2.0 版发布的

Open sourcing Pingora: our Rust framework for building programmable network services

正如我们在上一篇博客文章所述,Pingora 是一个 Rust 异步多线程框架,用于帮助我们构建 HTTP 代理服务。自我们上一篇博客文章以来,Pingora 已经在我们的全球网络中处理了近千万亿次互联网请求。

我们将 Pingora 开源,旨在帮助在我们自己的基础设施之外构建更好、更安全的互联网。我们希望为我们的客户、用户和其他人提供工具、想法和灵感,使用一个内存安全框架来构建自己的互联网基础设施。鉴于整个行业美国政府日益意识到内存安全的重要性,拥有这样一个框架尤其重要。在这个共同目标下,我们与互联网安全研究组织 ISRG 的 Prossimo 项目合作,帮助推动 Pingora 在互联网最关键的基础设施中得到采用。

上一篇博客文章中,我们讨论了构建 Pingora 的原因和方式。本期我们将讨论为什么以及如何使用 Pingora。

Pingora 不仅为代理、还为客户端和服务器提供构建块。除了这些组件,我们还提供一些实用库用于实现事件计数、错误处理和缓存等常见逻辑。

框架内容

Pingora 提供在 HTTP/1 和 HTTP/2、 TLS 或仅 TCP / UDP 之上构建服务的库和 API。作为代理,它支持 HTTP/1 和 HTTP/2 端到端、gRPC 和 websocket 代理。(HTTP/3 支持在路线图上)。它还提供可自定义的负载平衡和故障转移策略。在合规性和安全性方面,它同时支持常用的 OpenSSL 和 BoringSSL 库,两者均符合 FIPS 要求并支持后量子加密

除了提供以上功能,Pingora 还提供过滤器和回调函数,让用户完全自定义服务应该如何处理、转换和转发请求。这些 API 对于 OpenResty 和 NGINX 用户来说应该很熟悉,因为许多 API 直观地映射到 OpenResty 的 “*_by_lua” 回调函数上。

在运行方面,Pingora 提供零停机优雅重启,以便在不丢失任何传入请求的情况下进行自身升级。Syslog、Prometheus、Sentry、OpenTelemetry 和其他必备的可观察性工具也可以轻松与 Pingora 集成。

谁可受益于 Pingora

对于如下情况,您应该考虑 Pingora:

**安全是您的首要考虑因素:**对于用 C/C++ 编写的服务而言,Pingora 是一种内存安全性更高的替代方案。虽然有些人可能会争论编程语言之间的内存安全问题,但根据我们的实践经验,我们发现自己犯下导致内存安全问题的编码错误的可能性要小得多。此外由于我们花在解决这些问题上的时间更少,我们在实施新功能时更有成效。

**您的服务对性能敏感:**Pingora 快速高效。正如我们在上一篇博客文章中所述,由于 Pingora 的多线程架构,我们节省了大量 CPU 和内存资源。就对系统成本和/或速度敏感的工作负载而言,时间和资源的节省可能非常引人注目。

**您的服务需要广泛的定制:**Pingora 代理框架提供的 API 是高度可编程的。对于希望构建自定义高级网关或负载平衡器的用户,Pingora 提供了强大但简单的实施方法。我们将在下一节中提供示例。

让我们构建一个负载平衡器

让我们通过构建一个简单的负载均衡器来探索 Pingora 的可编程 API。该负载均衡器将以轮询的方式在 https://1.1.1.1/https://1.0.0.1/  之间选择上游。

首先让我们创建一个空白的 HTTP 代理。

任何实现 ProxyHttp 特征(类似于 C++ 或 Java 中接口的概念)的对象都是 HTTP 代理。那里唯一需要的方法是 upstream_peer(),对于每个请求都会调用这个方法。这个函数应该返回一个 HttpPeer,包含要连接的源 IP 以及连接方式。

pub struct LB();

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        todo!()
    }
}

接下来让我们实现轮询选择。Pingora 框架已经提供了 LoadBalancer,其中包含常见的选择算法,例如轮询和哈希,所以让我们直接使用它。如果用例需要更复杂或定制化的服务器选择逻辑,用户可以在这个函数中自行实现。

由于我们要连接到 HTTPS 服务器,还需要设置 SNI。如果需要,证书、超时和其他连接选项也可以在 HttpPeer 对象中设置。

pub struct LB(Arc<LoadBalancer<RoundRobin>>);

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        let upstream = self.0
            .select(b"", 256) // hash doesn't matter for round robin
            .unwrap();

        // Set SNI to one.one.one.one
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}

最后,让我们将服务投入运行。在这个示例中,我们硬编码了源服务器的 IP 地址。在实际工作负载中,当调用 upstream_peer() 时或在后台中,也可以动态发现源服务器的 IP 地址。创建服务后,我们只需告诉 LB 服务监听 127.0.0.1:6188。最后,我们创建了一个 Pingora 服务器,该服务器将是运行负载平衡服务的进程。

让我们尝试一下:

fn main() {
    let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();

    let mut lb = pingora_proxy::http_proxy_service(&my_server.configuration, LB(upstreams));
    lb.add_tcp("127.0.0.1:6188");

    let mut my_server = Server::new(None).unwrap();
    my_server.add_service(lb);
    my_server.run_forever();
}

我们可以看到代理正在工作,但源服务器以 403 拒绝我们。这是因为我们的服务只是简单地代理了 curl 设置的 Host 头部 127.0.0.1:6188,这让源服务器感到不悦。我们如何使代理修正这个问题?这可以简单地通过添加另一个名为 upstream_request_filter 的过滤器来完成。在连接到源服务器之后,发送任何 HTTP 请求之前,过滤器在每个请求上运行。我们可以在这个过滤器中添加、删除或更改 HTTP 请求头。

curl 127.0.0.1:6188 -svo /dev/null
> GET / HTTP/1.1
> Host: 127.0.0.1:6188
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden

我们再试一次:

async fn upstream_request_filter(…, upstream_request: &mut RequestHeader, …) -> Result<()> {
    upstream_request.insert_header("Host", "one.one.one.one")
}

这次成功了!完整示例请参见此处

curl 127.0.0.1:6188 -svo /dev/null
< HTTP/1.1 200 OK

下面是一个非常简单的图示,展示在本示例中请求如何通过回调和过滤器流动。Pingora 代理框架目前提供了更多的过滤器和回调,可以在请求的不同阶段允许用户修改、拒绝、路由和/或记录请求(和响应)。

在幕后,Pingora 代理框架负责连接池、TLS 握手、读取、写入、解析请求和其他常见代理任务,以便用户可以专注于对他们重要的逻辑。

开源,现在与未来

Pingora 是一个库和工具集,而不是可执行二进制文件。换句话说,Pingora 是驱动汽车的引擎,而不是汽车本身。虽然 Pingora 已经生产就绪,可供业界使用,但我们理解很多人希望有一种一揽子、即插即用的 Web 服务,具备低代码或无代码配置选项。我们与 ISRG 合作的重点将是在 Pingora 之上构建这样的应用,以扩大 Pingora 的影响力。请继续关注该项目的后续公告。

其他需要牢记的注意事项:

  • **目前,API 的稳定性不能保证。**尽管我们会尽量减少进行破坏性更改的频率,但在库的演变过程中,特别是在 1.0 版本前,我们仍保留添加、删除或更改请求和响应过滤器等组件的权利。

  • **对非 Unix 操作系统的支持目前不在路线图中。**尽管我们目前没有支持这些系统的计划,但未来情况可能会发生变化。

如何贡献

欢迎在我们的 GitHub 问题跟踪器中提出错误报告、文档问题或功能请求。在提交拉取请求之前,我们强烈建议您先阅读我们的贡献指南

总结

本篇博客文章中,我们宣布了 Pingora 框架的开源。我们展示了互联网实体和基础设施可受益于 Pingora 的安全性、性能和可定制性。我们还展示了 Pingora 的易用性和可定制性。

无论您是在构建生产 Web 服务,还是试验网络技术,我们都希望您能在 Pingora 中找到价值。这是一段漫长的旅程,但与开源社区分享这个项目从一开始就是一个目标。我们希望感谢 Rust 社区,因为 Pingora 是使用许多优秀的开源 Rust 包构建的。迁移到内存安全的互联网似乎是一个不可能的旅程,但我们希望您加入我们的行列。

我们保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的过程

从任何设备访问 1.1.1.1,以开始使用我们的免费应用程序,帮助您更快、更安全地访问互联网。要进一步了解我们帮助构建更美好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位
Developer Platform开发人员RustOpen SourcePerformance

在 X 上关注

Cloudflare|@cloudflare

相关帖子

2024年10月31日 13:00

Moving Baselime from AWS to Cloudflare: simpler architecture, improved performance, over 80% lower cloud costs

Post-acquisition, we migrated Baselime from AWS to the Cloudflare Developer Platform and in the process, we improved query times, simplified data ingestion, and now handle far more events, all while cutting costs. Here’s how we built a modern, high-performing observability platform on Cloudflare’s network. ...