2个版本
new 0.34.0+copy.33b0b21 |
|
---|
#1116 in 网络编程
189 每月下载量
在 logimesh 中使用
255KB
5.5K SLoC
tarpc
免责声明:这不是一个官方的Google产品。
tarpc是一个以易用性为重点的Rust RPC框架。定义一个服务只需要几行代码,并且大多数编写服务器的样板代码都由你处理。
什么是RPC框架?
"RPC"代表“远程过程调用”,是一种在别处执行产生返回值的函数调用。当调用RPC函数时,在幕后,函数会联系其他某个进程,并请求它们评估该函数。原始函数随后返回由其他进程产生的值。
RPC框架是大多数面向微服务的架构的基本构建块。两个著名的例子是gRPC和Cap'n Proto。
tarpc与其他RPC框架的不同之处在于,它在代码中定义模式,而不是在单独的语言(如.proto)中。这意味着没有单独的编译过程,也没有在不同语言之间切换上下文。
tarpc的其他一些功能
- 可插拔传输:任何实现了
Stream<Item = Request> + Sink<Response>
的类型都可以用作连接客户端和服务器传输。 Send + 'static
可选:如果传输不需要,tarpc 也不需要!- 级联取消:取消一个请求将向服务器发送一个取消消息。服务器将停止对请求的任何未完成工作,随后取消其自己的请求,并对整个传递依赖链重复此操作。
- 可配置的截止时间和截止时间传播:如果未指定,请求截止时间默认为10秒。当截止时间经过时,服务器将自动停止工作。服务器发送的任何使用请求上下文的请求都将传播请求截止时间。例如,如果服务器正在处理一个10秒的截止时间的请求,进行了2秒的工作,然后向另一个服务器发送请求,那个服务器将看到8秒的截止时间。
- 分布式跟踪:tarpc 使用扩展了 tracing 基本原理的 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.34", features = ["tokio1"] }
tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] }
在以下示例中,我们使用进程内通道在客户端和服务器之间进行通信。在实际代码中,您可能会通过网络进行通信。有关更实际的示例,请参阅 example-service。
首先,让我们设置依赖项和服务定义。
use futures::prelude::*;
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())
// Handle all requests concurrently.
.for_each(|response| async move {
tokio::spawn(response);
}));
// WorldClient is generated by the #[tarpc::service] attribute. It has a constructor `new`
// that takes a config and any Transport as input.
let mut 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
依赖项
~7–17MB
~198K SLoC