Introducing may_rpc

I’d like to introduce the may_rpc project which is a coroutine based RPC framework for rust that powered by may. It’s inspired by tarpc which has more detailed documentation.

may_rpc allows users write coroutine style code for server/client logic which is very efficient.

How to use the framework

Declare the rpc specification

1
2
3
4
5
6
7
8
9
rpc! {
/// the connection type, default is Tcp
/// valid types: Udp, Tcp, Multiplex
net: Multiplex;
/// Say hello
rpc hello(name: String) -> String;
/// add two number
rpc add(x: u32, y: u32 ) -> u32;
}

The rpc! macro expands to a collection of items that form an rpc service. In the above example, the rcp! macro is called with a user supplied rcp spec. This will generate RpcClient type, RpcServer type and RpcSpec trait. These generated types make it easy and ergonomic to write server and client without dealing with sockets or serialization directly. Just simply implement the generated traits for sever, that’s it.

when net type is Multiplex, you can freely clone the client instance without multiple connections to the server.

Implement RpcSpec for server

1
2
3
4
5
6
7
8
9
10
11
struct HelloImpl;

impl RpcSpec for HelloImpl {
fn hello(&self, name: String) -> String {
name
}

fn add(&self, x: u32, y: u32) -> u32 {
x + y
}
}

Create the server

1
let server = RpcServer(HelloImpl).start("127.0.0.1:3333").unwrap();

The returned server is just a coroutine handle. You can shut down it later if necessary.

Create a client and call the service

1
2
3
let client = RpcClient::connect("127.0.0.1:3333").unwrap();
assert_eq!(&client.hello(String::from("may_rpc")).unwrap(), "may_rpc");
assert_eq!(client.add(10, 32).unwrap(), 42);

Shut down the service gracefully

1
2
unsafe { server.coroutine().cancel() };
server.join().ok();

This will kill all the coroutines that spawned by the server even they are not finished.

You can see more examples in the project.

Performance

Just run the throughput example under may_rcp, the service is return immediately after receiving a request, so the result is just the framework’s maximum limit.

Machine Specs:

  • Logical Cores: 4 (4 cores x 2 threads)
  • Memory: 4gb ECC DDR3 @ 1600mhz
  • Processor: CPU Intel(R) Core(TM) i7-3820QM CPU @ 2.70GHz
  • Operating System: Windows 10

Test config:

To fully utilize the CPU, I use the following config on my laptop. Normally it will reach the maximum speed with workers:io_workers = 3:2, you can tune yours accordingly.

1
may::config().set_workers(6).set_io_workers(4);

result:

1
2
3
4
$ cargo run --example=throughput --release
......
Running `target\release\examples\throughput.exe`
206127.39 rpc/second

Easy coding with high performance, enjoy your life!