今天,我们隆重宣布开源 Pingora——我们一直用来构建网络服务的 Rust 框架,这些服务驱动着 Cloudflare 上的大部分流量。Pingora 是根据 Apache 许可证 2.0 版发布的。
正如我们在上一篇博客文章所述,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 包构建的。迁移到内存安全的互联网似乎是一个不可能的旅程,但我们希望您加入我们的行列。