#rpc #microservices #networking #api #server-api #remote-procedure #server

nightly tarpc-lib

一个以易用性为重点的Rust RPC框架

9个版本 (破坏性更新)

0.7.0 2019年9月21日
0.6.1 2019年5月22日
0.5.0 2019年4月30日
0.4.0 2019年4月16日
0.1.0 2018年10月17日

#15 in #remote-procedure

每月21次下载
tarpc-bincode-transport中使用

MIT许可证

110KB
2.5K SLoC

Build Status Latest Version Join the chat at https://gitter.im/tarpc/Lobby

tarpc

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

tarpc是一个以易用性为重点的Rust RPC框架。定义一个服务只需几行代码,大多数编写服务器时的模板代码都由它处理。

文档

什么是RPC框架?

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

RPC框架是大多数面向微服务架构的基本构建块。其中两个著名的例子是gRPCCap'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的传输

#[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

依赖项

~2.2–3.5MB
~58K SLoC