tonic_server_dispatch/lib.rs
1//! A typical architecture of network service is that after receiving a
2//! request, the network tasks dispatch it to the business tasks according
3//! to some fields. In this way, requests for the same content can be
4//! dispatched in the same task to avoid shared state or locking.
5//! This [tokio tutorial] gives detailed description.
6//!
7//! The same is true in `tonic`'s gRPC server. The dispatch of requests
8//! from network tasks to the business task has a pattern. This crate is
9//! an abstraction of this pattern to simplify the repetitive work in
10//! the application.
11//!
12//!
13//! # Async vs Sync
14//!
15//! The business jobs can run in async or sync mode.
16//!
17//! The [tokio tutorial] talks about the async mode. The business jobs run
18//! as tokio-tasks above the tokio runtime:
19//!
20//! network +--+ +--+ +--+ channels +--+ +--+ +--+ business
21//! tasks | | | | | | <----------> | | | | | | tasks*
22//! +--+ +--+ +--+ +--+ +--+ +--+
23//! tokio +----------------------------------------+
24//! runtime | |
25//! +----------------------------------------+
26//! +---+ +---+ +---+
27//! threads | | | | ... | |
28//! +---+ +---+ +---+
29//!
30//! If your business jobs contains no async code, then they can also
31//! run as native threads:
32//!
33//! network +--+ +--+ +--+ +---+ +---+ +---+ business
34//! tasks | | | | | | | | | | | | threads*
35//! +--+ +--+ +--+ | | | | | |
36//! tokio +------------+ channels | | | | | |
37//! runtime | | <----------> | | | | | |
38//! +------------+ | | | | | |
39//! +---+ +---+ | | | | | |
40//! threads | |... | | | | | | | |
41//! +---+ +---+ +---+ +---+ +---+
42//!
43//! This crate supports both modes.
44//!
45//!
46//! # Usage
47//!
48//! Let's take the [DictService in async-mode] as example. The [sync-mode]
49//! is similar.
50//!
51//! We assume that you are familiar with how to implement the original
52//! tonic server. Here we just talk about the parts related to this
53//! crate.
54//!
55//! 0. Add this Crate
56//!
57//! Add this crate and `paste` to your Cargo.toml.
58//!
59//! ``` toml
60//! tonic-server-dispatch = "*"
61//! paste = "1.0"
62//! ```
63//!
64//! 1. Define your Service
65//!
66//! This macro builds the mapping relationship between tonic
67//! network tasks and your business tasks.
68//!
69//! ``` rust
70//! tonic_server_dispatch::dispatch_service_async! {
71//! DictService, // original service name
72//! key, // hash by this request field
73//!
74//! // service methods
75//! set(SetRequest) -> SetReply,
76//! get(Key) -> Value,
77//! delete(Key) -> Value,
78//! }
79//! ```
80//!
81//! This macro is the main part of this crate. Go to its
82//! [doc page] for more detail.
83//!
84//! For sync-mode, use `dispatch_service_sync!` instead.
85//!
86//! 2. Implement your Service
87//!
88//! Define your business context for each task, and implement
89//! `DispatchBackend` for it. `DispatchBackend` defines all service
90//! methods, similar to the original tonic ones.
91//!
92//! ``` rust
93//! #[derive(Default)]
94//! struct DictCtx (HashMap<String, f64>);
95//!
96//! impl DispatchBackend for DictCtx {
97//! async fn get(&mut self, req: Key) -> Result<Value, Status> {
98//! match self.0.get(&req.key) {
99//! Some(value) => Ok(Value { value: *value }),
100//! None => Err(Status::not_found(String::new())),
101//! }
102//! }
103//!
104//! // all other methods ...
105//! }
106//! ```
107//!
108//! Compare to the original tonic prototype:
109//!
110//! ```
111//! async fn get(&self, req: tonic::Request<Key>)
112//! -> Result<tonic::Response<Value>, tonic::Status>
113//! ```
114//!
115//! the difference:
116//!
117//! - `&self` -> `&mut self`
118//! - `tonic::Request<Key>` -> `Key`
119//! - `tonic::Response<Value>` -> `Value`
120//!
121//! For sync-mode, remove the `async` before `fn`.
122//!
123//! 3. Start your Service
124//!
125//! This starts backend tasks and creates channels.
126//! The requests are dispatched from network tasks to backend
127//! tasks by the channels, and the response are sent back by
128//! oneshot channels.
129//!
130//! ```
131//! let svc = start_simple_dispatch_backend::<DictCtx>(16, 10);
132//! ```
133//!
134//! As the function's name suggests, it just starts the simple
135//! kind of backend task, which just listen on the request channel.
136//! If you want more complex backend task (e.g. listen on another
137//! channel too), you have to create tasks and channels youself.
138//! However, the implementation of this function can also be used
139//! as your reference.
140//!
141//! Now we have finished the dispatch level. It is very simple, isn't it?
142//! Go [DictService in async-mode] and [sync-mode] for the full source code.
143//!
144//! [tokio tutorial]: https://tokio.rs/tokio/tutorial/channels
145//! [DictService in async-mode]: https://github.com/WuBingzheng/tonic-server-dispatch/blob/master/examples/src/server_async.rs
146//! [sync-mode]: https://github.com/WuBingzheng/tonic-server-dispatch/blob/master/examples/src/server_sync.rs
147//! [doc page]: macro.dispatch_service.html
148
149
150#[macro_use]
151mod sync;
152
153#[macro_use]
154mod r#async;