-
Notifications
You must be signed in to change notification settings - Fork 6
feat: introduce mill-rpc framework #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d41b9a3
e5163db
f133391
9e1074d
1f7c77d
3354381
6117105
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| [package] | ||
| name = "mill-rpc" | ||
| version.workspace = true | ||
| authors.workspace = true | ||
| edition.workspace = true | ||
| license.workspace = true | ||
| rust-version.workspace = true | ||
| description = "An Axum-inspired RPC framework built on Mill-IO" | ||
|
|
||
| [dependencies] | ||
| mill-rpc-core = { path = "mill-rpc-core" } | ||
| mill-rpc-macros = { path = "mill-rpc-macros" } | ||
| mill-io = { path = "../mill-io" } | ||
| mill-net = { path = "../mill-net" } | ||
| serde = { version = "1", features = ["derive"] } | ||
| bincode = "1" | ||
| log = "0.4" | ||
| mio = { version = "1", features = ["os-poll", "net"] } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| [dev-dependencies] | ||
| env_logger = "0.11" | ||
| criterion = { version = "0.5", features = ["html_reports"] } | ||
| serde_cbor = "0.11" | ||
|
|
||
| [[example]] | ||
| name = "calculator_server" | ||
| path = "examples/calculator_server.rs" | ||
|
|
||
| [[example]] | ||
| name = "calculator_client" | ||
| path = "examples/calculator_client.rs" | ||
|
|
||
| [[example]] | ||
| name = "echo_server" | ||
| path = "examples/echo_server.rs" | ||
|
|
||
| [[example]] | ||
| name = "echo_client" | ||
| path = "examples/echo_client.rs" | ||
|
|
||
| [[example]] | ||
| name = "kv_server" | ||
| path = "examples/kv_server.rs" | ||
|
|
||
| [[example]] | ||
| name = "kv_client" | ||
| path = "examples/kv_client.rs" | ||
|
|
||
| [[example]] | ||
| name = "multi_service_server" | ||
| path = "examples/multi_service_server.rs" | ||
|
|
||
| [[example]] | ||
| name = "multi_service_client" | ||
| path = "examples/multi_service_client.rs" | ||
|
|
||
| [[example]] | ||
| name = "concurrent_clients" | ||
| path = "examples/concurrent_clients.rs" | ||
|
|
||
| [[bench]] | ||
| name = "rpc_comparison" | ||
| harness = false | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,138 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # mill-rpc | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| An RPC framework built on [`mill-io`](../mill-io) and [`mill-net`](../mill-net). Define services declaratively, get type-safe clients and servers — no async runtime required. | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Features | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Zero async**: Handlers are plain synchronous functions | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Macro-driven**: `mill_rpc::service!` generates a module with server trait, client struct, and dispatch logic | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Selective generation**: Use `#[server]`, `#[client]`, or both (default) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Multi-service**: Host multiple services on a single port with automatic routing | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Pluggable codecs**: Bincode by default, extensible | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - **Binary wire protocol**: Efficient framing with one-way calls and ping/pong | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Quick Start | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Define a service | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service Calculator { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn add(a: i32, b: i32) -> i32; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn multiply(a: i64, b: i64) -> i64; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| This generates a `calculator` module containing: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - `calculator::Service`: trait to implement on the server | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - `calculator::server(impl)`: wraps your impl for registration | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - `calculator::Client`: struct with typed RPC methods | ||||||||||||||||||||||||||||||||||||||||||||||||||
| - `calculator::methods`: method ID constants | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Server | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||||||||||||||||||||||||||||||
| struct MyCalc; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| impl calculator::Service for MyCalc { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn add(&self, _ctx: &RpcContext, a: i32, b: i32) -> i32 { a + b } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn multiply(&self, _ctx: &RpcContext, a: i64, b: i64) -> i64 { a * b } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| fn main() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let event_loop = Arc::new(EventLoop::new(4, 1024, 100).unwrap()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| let _server = RpcServer::builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .bind("127.0.0.1:9001".parse().unwrap()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .service(calculator::server(MyCalc)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .build(&event_loop) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| event_loop.run().unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider documenting the EventLoop parameters. The example uses 📝 Suggested improvement fn main() {
- let event_loop = Arc::new(EventLoop::new(4, 1024, 100).unwrap());
+ // 4 worker threads, 1024 max events per poll, 100ms timeout
+ let event_loop = Arc::new(EventLoop::new(4, 1024, 100).unwrap());📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Client | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let transport = RpcClient::connect(addr, &event_loop).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let client = calculator::Client::new(transport, Codec::bincode(), 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| let sum = client.add(10, 25).unwrap(); // 35 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let prod = client.multiply(7, 8).unwrap(); // 56 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Selective Generation | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Generate only what you need: | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Server crate: no client code generated | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #[server] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service Calculator { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn add(a: i32, b: i32) -> i32; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Client crate: no server code generated | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #[client] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service Calculator { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn add(a: i32, b: i32) -> i32; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Both (default): for tests, examples, or single-binary apps | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service Calculator { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn add(a: i32, b: i32) -> i32; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Multi-Service Server | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #[server] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service MathService { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn factorial(n: u64) -> u64; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mill_rpc::service! { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| #[server] | ||||||||||||||||||||||||||||||||||||||||||||||||||
| service StringService { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn reverse(s: String) -> String; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| let _server = RpcServer::builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .bind(addr) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .service(math_service::server(MathImpl)) // service_id = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .service(string_service::server(StringImpl)) // service_id = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .build(&event_loop)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Client side: share one connection | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let math = math_service::Client::new(transport.clone(), codec, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let strings = string_service::Client::new(transport, codec, 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## Examples | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Terminal 1 # Terminal 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cargo run --example calculator_server cargo run --example calculator_client | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cargo run --example echo_server cargo run --example echo_client | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cargo run --example kv_server cargo run --example kv_client | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cargo run --example multi_service_server cargo run --example multi_service_client | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Self-contained stress test | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cargo run --example concurrent_clients | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| ## License | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| Licensed under the Apache License, Version 2.0. See [LICENSE](../LICENSE) for details. | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider updating the install snippet once published.
The path-based dependency
mill-rpc = { path = "mill-rpc" }is correct for development but won't work for external users. Update this to a version-based dependency once the crate is published to crates.io.🤖 Prompt for AI Agents