#service #async-trait #tower-middleware #service-request #async #future #tower-service

tower-async

Tower Async 是一个模块化和可重用的组件库,用于构建健壮的客户端和服务器。它是从原始 Tower 库中分叉出来的 "Async Trait"。

3 个不稳定版本

0.2.0 2023 年 11 月 20 日
0.1.1 2023 年 7 月 18 日
0.1.0 2023 年 7 月 17 日

1724网络编程

每月 38 次下载
tower-async-http 中使用

MIT 许可证

185KB
2.5K SLoC

Tower Async

Tower Async 是一个模块化和可重用的组件库,用于构建健壮的网络客户端和服务器。

Crates.io Documentation MIT licensed Build Status

分叉

Tower Async 是从 https://github.com/tower-rs/tower 分叉出来的,并利用 async traits 来简化事物,并使将异步函数集成到中间件变得更加容易。

如果您想看到一个使用 tower_async 相比 tower 更简单的示例,您可以看到这里的一个示例

使用 tower 的延迟服务: https://github.com/plabayo/tower-async/blob/4ae0c4747fac6cc69b27c87a7ea5cacdd8bab3fb/tower-async-bridge/src/into_async/async_layer.rs#L91-L169

同样的服务可以用 tower_async 按如下方式编写

#[derive(Debug)]
struct DelayService<S> {
    inner: S,
    delay: std::time::Duration,
}

impl<S> DelayService<S> {
    fn new(inner: S, delay: std::time::Duration) -> Self {
        Self { inner, delay }
    }
}

impl<S, Request> tower_async_service::Service<Request> for DelayService<S>
where
    S: tower_async_service::Service<Request>,
{
    type Response = S::Response;
    type Error = S::Error;

    async fn call(&self, request: Request) -> Result<Self::Response, Self::Error> {
        tokio::time::sleep(self.delay).await;
        self.inner.call(request)
    }
}

如果您将此与链接的 tower 版本进行比较,您可能同意,如果您不需要自己编写 future 状态机,那么事情将会简单得多,这正是我们最初引入 async 语法的原因。

当然,我确实承认,如果您利用了如 https://docs.rs/futures-util-preview/latest/futures-util/future/index.html 这样的 crate 提供的惊人的实用工具,您可以在不自己编写 future 的情况下编写几乎相同的代码。

然而,这并不总是可能的,这也意味着你需要(1)了解这一点,并且(2)将其作为一个依赖项拉取,以及它带来的所有内容。而在现实中,你真的只想能够以 async 方式编写你的中间件。

我们充分认识到tower 不得不采用其使用的方法,因为在许多年里,这简单是唯一合理的做法,除非你想强迫你的用户使用 https://docs.rs/async-trait/latest/async_trait/,这不是每个人都愿意做出的选择,有时他们甚至可能没有这样的奢侈去做。

欢迎加入我们的 Discord,在 Discord#tower-async 公共频道,或者直接在 Tokio 的 Tower Discord 中@@glendc

在适当的情况下,我们将(手动)与 Tower 保持同步,如果有机会,我们也会“向上游”做出贡献。然而,鉴于我们的多样性很大,我们不确定这有多可能。

这组库最适合在自己的生态系统中使用,也就是说,只使用 tower-async 库及其依赖项。至少,我们希望 tower-async 成为需要时使用 tower(经典)(中间件)层的木偶。

如果你想了解如何在 tower-async 环境中纯操作,可以探索 Rama 代码库,这是一个代理框架,完全使用 tower-async 思维方式编写的,也是启动此分叉的主要动机。

然而,你也可以以其他方式将 towertower-async 连接起来。请参阅“连接到 Tokio 的官方 Tower 生态系统”章节以获取更多关于如何做到这一点的信息。

概述

Tower Async 旨在尽可能简单易用地构建健壮的网络客户端和服务器。它是协议无关的,但设计在请求/响应模式周围。如果你的协议完全是流式的,那么 Tower 可能不适合。

Tower Async 提供了一个简单的核心抽象,即 Service 特性,它表示一个异步函数,接受一个请求并返回响应或错误。这个抽象可以用来模拟客户端和服务器。

像超时这样的通用组件可以建模为包装某些内部服务的 Service,并在调用内部服务之前或之后应用额外的行为。这允许以协议无关和可组合的方式实现这些组件。通常,这些服务被称为 中间件

增加了一种抽象,即Layer trait,用于将中间件与Service组合。如果将Service视为从请求类型到响应类型的异步函数,则Layer是一个函数,它接受一个类型的Service并返回一个不同类型的Service。使用ServiceBuilder类型,可以通过组合多个Layer来向服务添加中间件。

与Tokio官方Tower生态系统有何不同?

  • 使用Async TraitsRFC-3185: Static async fn in traits)而不是要求用户手动实现Futures;
    • 这实际上迫使用户将依赖于不可命名的Futures的服务装箱,例如,用户可能需要使用来自更广泛的Tokio生态系统中的常用实用函数时遇到的由async functions返回的服务;
  • 放弃poll_ready的概念(参见常见问题解答)。
  • 使用&self代替&mut self来调用Service::call,以简化其使用;
    • 这使其更易于使用;
    • 明确指出用户负责适当的资源共享;
    • 使其更符合生态系统(例如,hyper(v1)也通过&self获取服务);

连接到Tokio的官方Tower生态系统

您可以使用此存储库中位于./tower-async-bridge目录的tower-async-bridge crate,并在crates.io上以相同名称发布。

从高层次来看,它允许您

请检查该软件包的单元测试和示例,以了解如何具体使用该软件包来实现此目的。

此外,我们敦促您仅将此类方法用作过渡目的,而不是作为永久的生活方式。我们认为最好的方法是使用一个或另一个,而不是将两者结合。但如果您绝对必须结合使用一个与其他,tower-async-bridge应该允许您做到这一点。

塔异步生态系统

塔异步由以下软件包组成

由于ServiceLayer特质对于所有使用塔的库都是重要的集成点,因此它们尽可能保持稳定,并且很少进行破坏性更改。因此,它们定义在单独的软件包中,tower-async-servicetower-async-layer。此软件包包含这些核心特质的重新导出,常用中间件的实现,以及与ServiceLayer一起工作的实用工具

tower-async-bridge用于将Tokio的官方塔生态系统与这个(异步特质)版本(分支)连接起来。

使用tower-async-test可以非常容易地进行Layer的单元测试。

最后,如果您使用tower-async进行HTTP目的(例如HTTP Web服务器),那么您可能会发现同时使用tower-async-http很有用,因为它提供了专门针对HTTP目的的构建器扩展和中间件。

用法

塔(异步)提供了一层抽象,以及各种中间件的泛型实现。这意味着仅靠tower-async软件包本身不提供网络客户端或服务器的有效实现。相反,塔的Service特质提供应用程序代码、提供中间件实现的库以及实现各种网络协议服务器和/或客户端的库之间的集成点。

根据您的特定用例,您可能以多种方式使用塔

库支持

以下是一些使用Tower Async(而不是Tower)和 Service 特性的库

  • rama:一个匿名化您的网络流量的代理框架。

如果您是支持Tower Async的crate的维护者,我们很乐意将您的crate添加到此列表!请 提交一个PR,添加对您库的简要描述!

入门指南

此crate提供的各种中间件实现都带有功能标志,以便用户只能编译他们需要的Tower部分。默认情况下,所有可选中间件都被禁用。

要开始使用Tower的所有可选中间件,请将以下内容添加到您的 Cargo.toml

tower-async = { version = "0.2", features = ["full"] }

或者,您可以选择只启用某些功能。例如,要仅启用 timeout 中间件,请编写

tower-async = { version = "0.2", features = ["timeout"] }

有关Tower提供的中介件的完整列表,请参阅 此处

浏览 tower-async-http/examples 中的示例,了解如何使用 tower-async 和其相关crate的一些示例。虽然这些示例主要集中在http上,但请注意

  • tower-async 可用于任何请求-响应流程(类似于 tower);
  • 您还可以使用 tower-async 与http web服务一起使用,而无需使用 tower-async-http crate,它只是为了提供针对http特定目的的额外中间件,但这完全是可选的。

文档还包含一些更小的示例,当然,您也可以阅读代码库及其单元测试。

支持的Rust版本

目前Tower Async需要nightly Rust,并且目前没有向后兼容性的承诺。

一旦 async traits 稳定,我们将再次开始支持稳定Rust,并开始努力实现向后兼容性。

有关Rust语言核心团队此路线图的信息,请参阅 https://blog.rust-lang.net.cn/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html

赞助

定期和一次性赞助商 alike 帮助我们支付由所有Plabayo的免费和开源工作产生的开发和运营成本。

我们也是Tokio的月度赞助商,以回报他们所做和持续努力的所有出色工作。

您可以在https://github.com/sponsors/plabayo了解更多关于Plabayo赞助信息。

一次性赞助(即所谓的“买我一杯咖啡”,但通过GitHub Sponsors支付),就像常规赞助一样受欢迎。并不是每个人都具备赞助的财务能力,所以请随意以其他任何您能想到的方式进行贡献。

常见问题解答(FAQ)

Tower的Service中的poll_ready方法来自哪里?

为了简化和因为该分支的作者认为这超出了范围的问题,该方法已被移除。

  • 大多数Tower服务和层根本不需要poll_ready方法,只是简单地调用内部服务来实现这一点;
  • 出于某些背压目的,您确实想了解请求以便知道如何处理它,所以poll_ready不适用于这些情况;

poll_ready也被用于负载均衡服务,但这被认为超出了范围。

  • 在我们的观点中,负载均衡传入的网络流更适合由您服务的周边网络基础设施(使用一个...负载均衡器)来处理;
  • 再次……如果您确实想在服务内部进行负载均衡,那可能是因为您实际上需要请求的上下文来知道该做什么,在这种情况下,poll_ready对您来说不起作用;

如果您在Tower(异步)服务内部仍想应用某种速率限制、背压或负载均衡,您应该在call函数内部进行。

然而,这个分支仍处于早期阶段,所以如果您对这个话题有不同的看法,请随意开始讨论。这个库的作者始终欢迎反馈,但保留拒绝任何他们希望拒绝的请求的权利。

我的最喜欢的Tower实用工具在哪里?

由于所有tower代码都必须手动移植,可能缺少一些功能。tower生态系统也继续繁荣发展,因此那里可能还会添加新的功能。如果您希望添加/移植此类功能,请随时与我们交谈或在GitHub上提交问题。

请注意,某些功能是故意不支持

  1. 所有与“ready”相关的功能都有意移除,因为我们认为这超出了范围。
  • 因此,也不支持依赖于此或在此基础上构建的所有实用工具。

请参阅前面的FAQ,以了解我们关于负载均衡等问题的观点。

许可证

该项目根据MIT许可证授权。

原始Tower作者表示衷心的感谢和致意,他们的代码也以相同的许可证类型授权。

贡献

除非您明确说明,否则您有意提交以供Tower Async包含的任何贡献,均应按照MIT许可证授权,不附加任何其他条款或条件。

依赖项

~0–1.4MB
~25K SLoC