Tonic: Server side service level Routing

Created on 3 Oct 2019  路  5Comments  路  Source: hyperium/tonic

Tracking issue for request Routing

Tonic should allow a single socket/server to serve multiple services. Routing is the way to get there.

Current progress: https://github.com/hyperium/tonic/blob/master/tonic-interop/src/bin/server.rs#L73

See also: https://github.com/tower-rs/tower-grpc/issues/2

crattonic enhancement help wanted

All 5 comments

@rlabrecque thanks for opening this!

We've had some ideas there are two that stand out to me currently:

1) Use a macro to generate the code that looks similar to the one in the interop server.

Server::builder()
    .serve(addr, route! {
        "test_service" => TestService::default(),
        "unimplemented_service" => UnimplementedService::default()
    })
    .await?;

2)

let routed_svc = tonic::Router::new(TestService::default())
    .with(UnimplementedService::default())

Server::builder()
    .serve(addr, routed_svc)
    .await?;

This one is more of an idea, and I'm not sure how to accomplish it without a ton of boxing 馃槩

Just wanted to throw in my use case for this:

We have a pretty decent REST setup in go, and one thing that I'm missing with tower-grpc is routing. grpc-router above is working alright for my needs now, but I don't think it's going to scale to our REST setup.

The biggest part missing would be the ability to add routes later, and then get a list of the routes.

Here's what I'd want to write:

    let router_service = Router::new(
        GreeterService::new(),
        PingService::new(),
        SomeOtherService::new(),
    );

    // Add the APIListService and HealthCheckService after we created the router_service, and pass
    // it the router_service so that we can get a list of all of the services.
    let api_list_service = APIListService::new(&router_service);
    router_service.add(api_list_service);

    let healthcheck_service = HealthCheckService::new(&router_service);
    router_service.add(healthcheck_service);

    let mut server = Server::new(router_service);

    // Bind server to http2 server and continue as normally
    // ...
#[derive(Clone, Debug)]
pub struct APIListService {
    services: Vec<std::string::String>
}

impl APIListService {
    pub fn new(router: &Router) {
        server::APIListServer::new(APIListService { services: router.get_routes() } )
    }
}

Copied from: https://github.com/tower-rs/tower-grpc/issues/2#issuecomment-536231778

@rlabrecque what is your use case around needing to add services at a later point?

As I understand it, so that the api_list_service can:

  • have access to all the services registered before it
  • be served on the same server
  • not expose itself, or the healthcheck_service in api list reqs

Although this could be done without involving the router like the following (although it's not super ergonomic):

let greeter = GreeterService::new();
let ping = PingService::new();
let other = SomeOtherService::new();
let api_list = ApiListService::new()
    .with::<GreeterService>()
    .with::<PingService>()
    .with::<SomeOtherService>();
let healthcheck = HealthCheckService::new()
    .with::<GreeterService>()
    .with::<PingService>()
    .with::<SomeOtherService>()
    .with::<ApiListService>();

// boxed version
let router = Router::new()
    .with(greeter)
    .with(ping)
    .with(other)
    .with(api_list)
    .with(healthcheck);

// static version
let router = router! {greeter, ping, other, api_list, healthcheck};

let mut server = Server::new(router);

// Bind server to http2 server and continue as normally
// ...

So long as every service could expose its own routes. I've written a quick PoC that allows that via a trait and associated consts, something like:

pub mod router {
    // every MakeService gets a generated impl for:
    pub trait Routed {
        const PREFIX: &'static str;
        const ROUTES: &'static [&'static str];
    }
}

@nytopop That makes sense that was what I was kinda thinking. We can totally add that trait and implement it for codegen. Each async trait impl can include that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cthulhua picture cthulhua  路  3Comments

ebkalderon picture ebkalderon  路  5Comments

Jasper-Bekkers picture Jasper-Bekkers  路  6Comments

LucioFranco picture LucioFranco  路  5Comments

petratbtl picture petratbtl  路  7Comments