I'm new with RPC protocols so probably my question may have some conceptual errors.
I'm trying to write a higher level library for cap'n proto for writing both servers/clients, something like tonic but over cap'n proto instead protobuf and runtime agnostic.
I would like have an interface pretty similar to tonic. Something like this for the server side.
use my_lib::server::Server;
use crate::services::{SomeService, SomeService2}; // ...SomeService3, 4, 5 .........
#[async_std::main]
async fn main() ->async_std::io::Result<()> {
let service_1 = SomeService::new();
let service_2 = SomeService2::new();
Server::new()
.add_service(service_1)
.add_service(service_2) // add service 3, .. 4 ... 5 ... .... n
.serve("0.0.0.0:4567").await?;
Ok(())
}
To can call to whatever be, service_1 or service_2.
But I'm a bit (well actually more than a bit...) stuck.
I reproduced the hello_world example
#[async_std::main]
async fn main() -> async_std::io::Result<()> {
let hello_world_client: hello_world::Client = capnp_rpc::new_client(HelloWorldImpl);
let listener = TcpListener::bind("127.0.0.1:50070")
.await
.expect("failed to open listener");
let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
let stream = stream.expect("failed to unwrap stream");
let reader = stream.clone();
let writer = stream;
let network = twoparty::VatNetwork::new(
reader,
writer,
rpc_twoparty_capnp::Side::Server,
Default::default(),
);
let rpc_system = RpcSystem::new(Box::new(network), Some(hello_world_client.clone().client));
async_std::task::spawn_local(rpc_system).await.expect("");
}
Ok(())
}
This works well when I run the client. But I'm trying to figure out how to put more than one service in the same server. My intuition tells to me that the Server
struct should have a set of Services (but also I have no idea how to represent a service) maybe something like Vec<Service>
or maybe HashMap<_?_, Service>
so the incoming stream should "be matched" to their own service - But honestly I have no idea.
My first attempt was to simply create a hello_world_2 and have it placed in series next to hello_world, something like this.
And this does not work when you test it, only the client of the service that is placed first works.
I understand that this behavior has to do with the flow of how the stream is processed, when the client of the second service calls the server the only interface that can read is the one of the first service, this brings me back to the beginning of my question, how to handle multiple RPC interfaces on a single server?
I'm sorry if this question is a bit silly, but I don't really understand how to define what a service or route is.
You can declare a separate service which has methods to get all the other services:
interface MainService {
getFooService @0 () -> (foo :FooService);
getBarService @1 () -> (bar :BarService);
# ...
}
Then, make that the main service that you export.
Cap'n Proto allows one service's methods to accept or return other services. You can use Cap'n Proto's Promise Pipelining feature to avoid having to wait for a round trip to the server before you can start making calls on the returned services.