16个版本 (破坏性更新)

0.13.1 2024年1月21日
0.13.0 2023年12月30日
0.12.0 2021年9月22日
0.11.0 2021年4月14日
0.1.0 2017年3月31日

#16 in #rpc-service

Download history 28601/week @ 2024-04-05 27812/week @ 2024-04-12 30044/week @ 2024-04-19 25927/week @ 2024-04-26 25809/week @ 2024-05-03 26681/week @ 2024-05-10 21704/week @ 2024-05-17 22273/week @ 2024-05-24 27452/week @ 2024-05-31 26775/week @ 2024-06-07 25626/week @ 2024-06-14 24445/week @ 2024-06-21 24484/week @ 2024-06-28 24415/week @ 2024-07-05 25047/week @ 2024-07-12 19939/week @ 2024-07-19

每月下载 98,217
用于 260 个crate(直接使用3个)

MIT 许可证

30KB
566

Crates.io MIT licensed Build status Discord chat

tarpc

免责声明:这不是一个官方的Google产品。

tarpc是一个专注于易用性的Rust RPC框架。定义服务只需几行代码,大多数服务器编写的样板代码也由框架自动处理。

文档

什么是RPC框架?

"RPC"代表“远程过程调用”,是指在一个远程位置执行产生返回值的工作的函数调用。当调用RPC函数时,在幕后该函数会联系某个其他进程并请求它们评估该函数。原始函数随后返回由其他进程产生的值。

RPC框架是大多数面向微服务的架构的基本构建块。其中两个知名的框架是gRPCCap'n Proto

tarpc与其他RPC框架的不同之处在于它通过代码定义模式,而不是使用像.proto这样的单独语言。这意味着没有单独的编译过程,也没有在不同语言之间切换上下文的需要。

tarpc的其他一些特性

  • 可插拔传输:任何实现Stream<Item = Request> + Sink<Response>的类型都可以用作传输以连接客户端和服务器。
  • Send + 'static可选:如果传输不需要它,tarpc也不需要!
  • 级联取消:丢弃请求会向服务器发送取消消息。服务器将停止对请求的任何未完成工作,随后取消其自身的任何请求,并重复此过程,直至整个传递依赖链。
  • 可配置的截止时间和截止时间传播:如果未指定,请求截止时间默认为10秒。当截止时间过后,服务器将自动停止工作。服务器发送的任何使用请求上下文的请求都将传播请求截止时间。例如,如果一个服务器正在处理一个10秒的截止时间的请求,工作了2秒后,向另一个服务器发送请求,那么那个服务器将看到8秒的截止时间。
  • 分布式追踪:tarpc使用追踪原语,这些原语通过OpenTelemetry追踪进行了扩展。使用兼容的追踪订阅者,如Jaeger,每个RPC都可以通过客户端、服务器以及服务器下游的其他依赖项进行追踪。即使对于未连接到分布式追踪收集器的应用程序,仪器也可以由常规记录器,如env_logger,进行摄取。
  • Serde序列化:启用serde1 Cargo功能将使服务请求和响应Serialize + Deserialize。虽然这是可选的,但:内存传输也可以使用,因此当不需要时,不需要支付序列化的代价。

使用方法

将依赖项添加到您的Cargo.toml

tarpc = "0.34"

tarpc::service属性扩展为形成RPC服务的一系列项。这些生成的类型使得编写服务器更加简单和直观,减少了样板代码。只需实现生成的服务特质,就可以开始编写服务器了!

示例

此示例使用tokio,因此请将以下依赖项添加到您的Cargo.toml

anyhow = "1.0"
futures = "0.3"
tarpc = { version = "0.31", features = ["tokio1"] }
tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] }

在以下示例中,我们使用进程内通道在客户端和服务器之间进行通信。在实际代码中,您可能需要通过网络进行通信。有关更实际的示例,请参阅example-service

首先,让我们设置依赖项和服务定义。

use futures::future::{self, Ready};
use tarpc::{
    client, context,
    server::{self, Channel},
};

// This is the service definition. It looks a lot like a trait definition.
// It defines one RPC, hello, which takes one arg, name, and returns a String.
#[tarpc::service]
trait World {
    /// Returns a greeting for name.
    async fn hello(name: String) -> String;
}

此服务定义生成一个名为World的特质。接下来我们需要为我们的Server结构体实现它。

// This is the type that implements the generated World trait. It is the business logic
// and is used to start the server.
#[derive(Clone)]
struct HelloServer;

impl World for HelloServer {
    async fn hello(self, _: context::Context, name: String) -> String {
        format!("Hello, {name}!")
    }
}

最后,让我们编写我们的main,这将启动服务器。虽然此示例使用进程内通道,但tarpc还提供了一个位于serde-transport功能后面的通用serde_transport,在tcp功能后面提供了额外的TCP功能。

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let (client_transport, server_transport) = tarpc::transport::channel::unbounded();

    let server = server::BaseChannel::with_defaults(server_transport);
    tokio::spawn(server.execute(HelloServer.serve()));

    // WorldClient is generated by the #[tarpc::service] attribute. It has a constructor `new`
    // that takes a config and any Transport as input.
    let client = WorldClient::new(client::Config::default(), client_transport).spawn();

    // The client has an RPC method for each RPC defined in the annotated trait. It takes the same
    // args as defined, with the addition of a Context, which is always the first arg. The Context
    // specifies a deadline and trace information which can be helpful in debugging requests.
    let hello = client.hello(context::current(), "Stim".to_string()).await?;

    println!("{hello}");

    Ok(())
}

服务文档

像平常一样使用cargo doc来查看由service!调用展开的所有项创建的文档。

许可证:MIT

依赖项

~1.5MB
~35K SLoC