3 个版本 (重大更新)
0.3.0 | 2019 年 9 月 21 日 |
---|---|
0.2.0 | 2019 年 3 月 27 日 |
0.1.0 | 2018 年 10 月 16 日 |
#15 in #server-response
在 2 个 crate 中使用 (via tarpc-lib)
6KB
51 行
tarpc
免责声明: 这不是官方的 Google 产品。
tarpc 是一个 Rust RPC 框架,注重易用性。定义一个服务只需要几行代码,而且大多数服务器编写过程中的模板代码都由框架自动处理。
什么是 RPC 框架?
"RPC" 代表 "远程过程调用",即在某个地方完成返回值生成的工作。当调用 RPC 函数时,后台函数会联系其他进程,并要求它们评估该函数。原始函数随后返回其他进程生成的值。
RPC 框架是大多数面向微服务的架构的基本构建块。其中两个知名的是 gRPC 和 Cap'n Proto。
tarpc 与其他 RPC 框架的不同之处在于,它通过代码定义模式,而不是在单独的如 .proto 语言中定义。这意味着没有独立的编译过程,也没有不同语言之间的上下文切换。
tarpc 的其他一些特性
- 插件式传输:任何实现
Stream<Item = Request> + Sink<Response>
的类型都可以作为传输来连接客户端和服务器。 Send
可选:如果传输不需要它,tarpc 也不需要!- 级联取消:取消请求会向服务器发送取消消息。服务器将停止对该请求的任何未完成的工作,并取消其自身的请求,包括所有递归依赖项中的请求。
- 可配置的截止日期和截止日期传播:如果未指定,则请求截止日期默认为10秒。当截止日期过去后,服务器将自动停止工作。服务器发送的任何使用请求上下文的请求都会传播请求截止日期。例如,如果一个服务器正在处理一个10秒的截止日期的请求,做了2秒的工作,然后向另一个服务器发送请求,那个服务器将看到8秒的截止日期。
- Serde序列化:启用
serde1
Cargo功能将使服务请求和响应Serialize + Deserialize
。这是完全可选的:也可以使用内存传输,因此不必在不需要时支付序列化的代价。
使用方法
将依赖项添加到您的Cargo.toml
tarpc = "0.18.0"
tarpc::service
属性展开为一个由项组成的集合,这些项形成了一个rpc服务。这些生成的类型使得编写具有更少样板的服务变得简单且直观。只需实现生成的服务特质,你就可以开始了!
示例
对于此示例,除了tarpc之外,还需要将其他两个依赖项添加到您的Cargo.toml
futures-preview = "0.3.0-alpha.18"
tokio = "0.2.0-alpha.3"
在以下示例中,我们使用进程内通道进行客户端和服务器之间的通信。在实际代码中,你可能会通过网络进行通信。有关更实际的示例,请参阅example-service。
首先,让我们设置依赖项和服务定义。
use futures::{
future::{self, Ready},
prelude::*,
};
use tarpc::{
client, context,
server::{self, Handler},
};
use std::io;
// 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 {
// Each defined rpc generates two items in the trait, a fn that serves the RPC, and
// an associated type representing the future output by the fn.
type HelloFut = Ready<String>;
fn hello(self, _: context::Context, name: String) -> Self::HelloFut {
future::ready(format!("Hello, {}!", name))
}
}
最后,让我们编写我们的main
来启动服务器。虽然此示例使用进程内通道,但tarpc还提供了一个使用TCP上bincode的in-process channel
。
#[tokio::main]
async fn main() -> io::Result<()> {
let (client_transport, server_transport) = tarpc::transport::channel::unbounded();
let server = server::new(server::Config::default())
// incoming() takes a stream of transports such as would be returned by
// TcpListener::incoming (but a stream instead of an iterator).
.incoming(stream::once(future::ready(server_transport)))
.respond_with(HelloServer.serve());
tokio::spawn(server);
// WorldClient is generated by the macro. 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
依赖项
~485–690KB
~14K SLoC