Crate tonic_server_dispatch

Source
Expand description

A typical architecture of network service is that after receiving a request, the network tasks dispatch it to the business tasks according to some fields. In this way, requests for the same content can be dispatched in the same task to avoid shared state or locking. This tokio tutorial gives detailed description.

The same is true in tonic’s gRPC server. The dispatch of requests from network tasks to the business task has a pattern. This crate is an abstraction of this pattern to simplify the repetitive work in the application.

§Usage

Let’s take the DictService as example.

We assume that you are familiar with how to implement the original tonic server. Here we just talk about the parts related to this crate.

  1. Add this Crate

    Add this crate and paste to your Cargo.toml.

    tonic-server-dispatch = "*"
    paste = "1.0"
  2. Define your Service

    This macro builds the mapping relationship between tonic network tasks and your business tasks.

    dispatch_service! {
        DictService, // original service name
        key, // hash by this request field
    
        // service methods
        set(SetRequest) -> SetReply,
        get(Key) -> Value,
        delete(Key) -> Value,
    }

    This macro is the main part of this crate. Go to its doc page for more detail.

  3. Implement your Service

    Define your business context for each task, and implement DispatchBackend for it. DispatchBackend defines all service methods, similar to the original tonic ones.

    #[derive(Default)]
    struct DictCtx (HashMap<String, f64>);
    
    impl DispatchBackend for DictCtx {
        async fn get(&mut self, req: Key) -> Result<Value, Status> {
            match self.0.get(&req.key) {
                Some(value) => Ok(Value { value: *value }),
                None => Err(Status::not_found(String::new())),
            }
        }
    
        // all other methods ...
    }

    Compare to the original tonic prototype:

    async fn get(&self, req: tonic::Request<Key>)
        -> Result<tonic::Response<Value>, tonic::Status>

    the difference:

    • &self -> &mut self
    • tonic::Request<Key> -> Key
    • tonic::Response<Value> -> Value
  4. Start your Service

    This starts backend tasks and creates channels. The requests are dispatched from network tasks to backend tasks by the channels, and the response are sent back by oneshot channels.

    let svc = start_simple_dispatch_backend::<DictCtx>(16, 10);

    As the function’s name suggests, it just starts the simple kind of backend task, which just listen on the request channel. If you want more complex backend task (e.g. listen on another channel too), you have to create tasks and channels youself. However, the implementation of this function can also be used as your reference.

Now we have finished the dispatch level. It is very simple, isn’t it? Go DictService for the full source code.

Macros§

dispatch_service
Define the service and build the mapping relationship between tonic network tasks and your business tasks.