Skip to content
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

Multiple services on one server/port #153

Closed
Boscop opened this issue May 11, 2017 · 8 comments
Closed

Multiple services on one server/port #153

Boscop opened this issue May 11, 2017 · 8 comments

Comments

@Boscop
Copy link

Boscop commented May 11, 2017

I don't see a way to serve multiple services (for different 'concerns') on one server/port. Is there a way (like in gRPC)?

Similarly, if one client executable wants to use multiple services, can it reuse the same connection among the different instantiated clients? (like in gRPC)

@tikue
Copy link
Collaborator

tikue commented May 11, 2017

I think the former would follow from the latter, but we don't support the former. I was considering supporting it at one point but there was no demand and it seemed like a fairly nice feature. Can you tell me more about your requirements that could benefit from this?

@Boscop
Copy link
Author

Boscop commented May 11, 2017

We have an API with a lot of functions, and we want to group them by concerns into services like it's usually done with gRPC ('separation of concerns'). (The alternative is to use long underscore function names like concern_method() and put all in one service.)
We have different clients that use different (overlapping) subsets of all services. So it'd be 'nicer' if clients could use only the services that they need (the number of functions in total is quite large).
Every service would be a (sub-)API for its 'concern'.

@tikue
Copy link
Collaborator

tikue commented May 11, 2017

Ah ok, that makes sense! So I'd love to be able to support this use case, but it's a fairly radical departure from how things are done now. Right now tarpc is a pretty thin layer on top of tokio, which is nice from a maintenance perspective. Supporting multiple services per port would basically require a substantial rewrite, and I don't think I have the bandwidth for it any time soon.

@tikue
Copy link
Collaborator

tikue commented May 11, 2017

That's not to discourage anyone else from working on this!

@tikue
Copy link
Collaborator

tikue commented Oct 18, 2018

So....the substantial rewrite ended up happening. I believe you can now accomplish this in the transport layer, by having connecting clients do a handshake where they send a message with the service name. On the server side, there'd be a service registry, something like a HashMap<ServiceName, Sender<TcpStream>>, and it would forward new connections (I'm using TcpStream but it could really be whatever transport you want to use) to the appropriate service.

Are you interested in working this? I think it'd be pretty cool to get a demo of this working!

@tikue tikue mentioned this issue Oct 23, 2018
tikue added a commit that referenced this issue Oct 25, 2018
# Changes

## Client is now a trait
And `Channel<Req, Resp>` implements `Client<Req, Resp>`. Previously, `Client<Req, Resp>` was a thin wrapper around `Channel<Req, Resp>`.

This was changed to allow for mapping the request and response types. For example, you can take a `channel: Channel<Req, Resp>` and do:

```rust
channel
    .with_request(|req: Req2| -> Req { ... })
    .map_response(|resp: Resp| -> Resp2 { ... })
```

...which returns a type that implements `Client<Req2, Resp2>`.

### Why would you want to map request and response types?

The main benefit of this is that it enables creating different client types backed by the same channel. For example, you could run multiple clients multiplexing requests over a single `TcpStream`. I have a demo in `tarpc/examples/service_registry.rs` showing how you might do this with a bincode transport. I am considering factoring out the service registry portion of that to an actual library, because it's doing pretty cool stuff. For this PR, though, it'll just be part of the example.

## Client::new is now client::new

This is pretty minor, but necessary because async fns can't currently exist on traits. I changed `Server::new` to match this as well.

## Macro-generated Clients are generic over the backing Client.

This is a natural consequence of the above change. However, it is transparent to the user by keeping `Channel<Req, Resp>` as the default type for the `<C: Client>` type parameter. `new_stub` returns `Client<Channel<Req, Resp>>`, and other clients can be created via the `From` trait.

## example-service/ now has two binaries, one for client and one for server.

This serves as a "realistic" example of how one might set up a service. The other examples all run the client and server in the same binary, which isn't realistic in distributed systems use cases.

## `service!` trait fns take self by value.

Services are already cloned per request, so this just passes on that flexibility to the trait implementers.

# Open Questions

In the service registry example, multiple services are running on a single port, and thus multiple clients are sending requests over a single `TcpStream`. This has implications for throttling: [`max_in_flight_requests_per_connection`](https://github.com/google/tarpc/blob/master/rpc/src/server/mod.rs#L57-L60) will set a maximum for the sum of requests for all clients sharing a single connection. I think this is reasonable behavior, but users may expect this setting to act like `max_in_flight_requests_per_client`.

Fixes #103 #153 #205
@tikue
Copy link
Collaborator

tikue commented Oct 25, 2018

#204 provides an extended example for how to do this.

@tikue tikue closed this as completed Oct 25, 2018
@tqwewe
Copy link

tqwewe commented Dec 14, 2021

@tikue I am struggling to find an example anywhere in the project, has it been moved/removed? Any idea where I can go to find an example of this?

@tikue
Copy link
Collaborator

tikue commented Dec 15, 2021

@acidic9 I did remove some examples at some point! I don't know if I would endorse the particular example I was referencing, but if you're curious you can find it on the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants