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

[NEED HELP] How to call a service from another service? #62

Open
lquerel opened this issue Aug 26, 2022 · 3 comments
Open

[NEED HELP] How to call a service from another service? #62

lquerel opened this issue Aug 26, 2022 · 3 comments

Comments

@lquerel
Copy link

lquerel commented Aug 26, 2022

I am trying to simulate several services communicating via madsim RPC and I don't know how to call a service from another service. The Endpoint::call method is applied on an instance of Endpoint but I haven't found a way to get the Endpoint of the current service. See below a small example of what I'm trying to do.

#[derive(Serialize, Deserialize, Request)]
#[rtype("String")]
pub struct HttpGetRequest(pub String);

#[derive(Clone)]
pub struct FrontendService {
    redisAddr: SocketAddr,
}

#[madsim::service]
impl FrontendService {
    pub fn new(redisAddr: SocketAddr) -> Self {
        FrontendService {
            redisAddr,
        }
    }

    #[rpc]
    async fn serve_http_request(&self, req: HttpGetRequest) -> String {
        let reply = <CURRENT_ENDPOINT?>.call(self.redisAddr, redis::GetRedisRequest(req.0.clone())).await.unwrap();
        reply
    }
}

I tried multiple approaches:

  • The method server_on consumes the endpoint and Endpoint is not clonable so I can't pass the endpoint after the initial binding.
  • The creation of Endpoint from the method Endpoint::connect doesn't work in this context.
  • I can't use Arc as the serve_on method consumes an instance of Endpoint.

Is it a supported scenario?
If yes how can I do that?

If I can solve this problem, I will submit a PR containing an example of a microservices topology communicating via RPC and running on several simulated nodes. I think this kind of complete example doesn't exist yet in the repo and it would probably save time for future users. Feel free to point me to such an example if it exists.

@lquerel
Copy link
Author

lquerel commented Aug 26, 2022

By patching the #[madsim::service] macro code I was able to get an example that works. The modification is to change the signature of the serve_on method to an Arc instead of an Endpoint. This allows me to also pass a clone of Arc to my service to allow communication with another service. However, I think the approach could be improved by making a slightly more complex modification.

Approach compatible with the existing API: the idea would be to provide a method to retrieve a reference to the current endpoint.

Approach requiring a change in the API: the idea would be to change the signature of the methods annotated with #[rpc] and to add as first parameter a reference on the current endpoint.

I can see how to implement the second approach. For the first one I'm not sure how to do it.

What do you think about it?

@wangrunji0408
Copy link
Member

Hi, thanks for your interest on this project!

Microservices is definitely a scenario we want to support. But since we haven't applied madsim to such a project, the API of this part is not mature enough to use.

For this problem, personally I prefer to change the signature of the serve_on method to an Arc<Endpoint>. Or one more step, we can make Endpoint itself cheaply clonable, as several other crates did. I believe this would be more friendly to developers.

For the 2 approaches you mentioned, I'm not sure whether we should encourage users to reuse the endpoint from current service to communicate with another one. Because usually they would create a new endpoint for this. If only considering these 2 approaches, I would choose the first one but it doesn't seem to work. (The service does not own an endpoint so it's impossible to retrieve one.) The second approach changes the signature of all RPC handlers, so doesn't look to be a good solution to me.

Overall, in this scenario, I recommend putting an endpoint into the service structure to communicate with other service. It is OK whether itself is serving on the same endpoint or not.

If I can solve this problem, I will submit a PR containing an example of a microservices topology communicating via RPC and running on several simulated nodes. I think this kind of complete example doesn't exist yet in the repo and it would probably save time for future users. Feel free to point me to such an example if it exists.

Unfortunately we don't have such an example right now. I was trying to migrate madraft as an example but it has not finished yet. You are welcome to submit PRs for any improvements!

@lquerel
Copy link
Author

lquerel commented Aug 30, 2022

Thank you for your feedback. I will submit a PR in the next few days with this example and modification.

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

2 participants