I'm using tarpc to create a tcp service.
#[tarpc::service]
pub trait Hello {
async fn say_hi() -> String;
}
pub struct HelloServer;
#[tarpc::server]
impl Hello for HelloServer {
/// requires to call SingletonInstance::say_hi()
async fn say_hi(self, context: tarpc::context::Context) ->String {
return "hi".to_string();
}
}
/// I want HelloServer to be a proxy of SingletonInstance
/// The data flow like this:
/// client tarpc call -> helloServer.say_hi -> singletonInstance.say_hi -> send response through tarpc
///
pub struct SingletonInstance {}
impl SingletonInstance {
pub async fn say_hi() -> String {
return String::new()
}
}
Full Example https://github.com/divflex/demo-rpc
Question:
The HelloServer
is basiclly a proxy for the SingletonInstance
,so the say_hi
method need to call the same method on SingletonInstance
.
What's the proper way to call SingletonInstance::say_hi
?
My idea:
I created a tx
(tokio::sync::mpsc
) property on HelloServer
, so the instance can use self.tx.send()
to communicate with SingletonInstance
.But HelloServer also need a receiver to get results. This seems too complicated.
One way to call SingletonInstance::say_hi
from HelloServer
is to add a link/reference to SingletonInstance
in HelloServer
. You can do this by adding a field to the HelloServer
struct that holds an Arc<SingletonInstance>
and then use this reference to call the say_hi
method on the SingletonInstance
:
use std::sync::Arc;
pub struct HelloServer {
singleton: Arc<SingletonInstance>,
}
#[tarpc::server]
impl Hello for HelloServer {
async fn say_hi(self, context: tarpc::context::Context) -> String {
self.singleton.say_hi().await
}
}
pub struct SingletonInstance {}
impl SingletonInstance {
pub async fn say_hi(&self) -> String {
"hi".to_string()
}
}
This way, you can avoid using channels to communicate between HelloServer
and SingletonInstance
.
Another way is you create an instance of SingletonInstance
onto HelloServer
(but that's not what SingletonInstance means :)
use tarpc::context;
#[derive(Clone)]
pub struct HelloServer {
instance: SingletonInstance,
}
#[tarpc::server]
impl Hello for HelloServer {
async fn say_hi(self, _: context::Context) -> String {
self.instance.say_hi().await
}
}
pub struct SingletonInstance {}
impl SingletonInstance {
pub async fn say_hi(&self) -> String {
"hi".to_string()
}
}
Cloning is necessary because tarpc creates a new instance of the server for each connection, and we need to be able to clone the server to serve multiple clients.