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;