-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
chore: fix gRPC multiplex example #2825
base: main
Are you sure you want to change the base?
Conversation
let layer = tower::ServiceBuilder::new()
.layer(TraceLayer::new_for_grpc())
.into_inner();
let grpc = TonicServer::builder()
.layer(layer)
.add_service(deck_svc)
.into_router(); Here's an example
Scratch all of that. I just needed to put |
@spencerbart trivial point, but you can add your TraceLayer before calling Server::builder()
.layer(TraceLayer::new_for_grpc()) // <--
.add_service(your_awesome_service)
.into_router() |
@maxnachlinger That's what I had before and it didn't work. For some reason calling |
@spencerbart hah, sorry for the churn man :) |
Yep, I agree that this is very much a minimal POC, and the actual interaction between the grpc and rest server and its associated middleware isn't really that great... In an ideal world, we should be able to merge the routers together and route based on Content-type, which would needs its own middleware. I think it's possible but I haven't investigated it. |
I have been playing with this, but I have not (yet) solved how to work with layers using this example. As you already commented, when using |
You're absolutely right. Thanks for the pull that allowed me to have the grpc service and http service run on the same port in tonic 0.12.1 release. Here is the crates package I used: tonic = "0.12.1"
prost = "0.13.1"
tokio = {version = "1.38.0",features = ["full"]}
tower = { version = "0.4.13", features = ["steer"] }
# note: Must be the same as the tonic version
tonic-reflection = "0.12.1"
# note: Must be the same as the tonic version
tonic-build = "0.12.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, sorry for letting this linger for so long!
I know next to nothing about GRPC, so it's hard for me to review this. @mladedav how about you?
# needs to match tonic | ||
axum = { version = "0.7.5" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't want examples within this repo to pull in a crates.io release of axum. I'm considering moving examples to a separate repo anyways though, see #2942 for discussion.
I used the solution mentioned above to merge multiple services into one service. This solution is indeed feasible. See the project link below for details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did use tonic before, but not like this so I still want to take a closer look to some of the parts used here, but I'd say this looks good.
I want to also double check if we can't make it work with a different versions of Axum between us and tonic. If we can't I'd vote to grant an exception here to depend on crates.io especially if we want to move it soon afterwards anyway.
.map(|content_type| content_type.as_bytes()) | ||
.filter(|content_type| content_type.starts_with(b"application/grpc")) | ||
.is_some() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, you could do just .and_then(|content_type| content_type.as_bytes().starts_with(b"...").then_some(1)).unwrap_or(0)
without an explicit condition instead.
(Although now I see you have just copied this from the deleted file)
I could probably do a cargo patch to resolve this. Lemme give it a shot |
If you mean doing I see two options to reconcile different
let grpc = Router::new().nest_service(
"/",
grpc.into_service()
.map_response(|response| response.map(Body::new)),
); Then you can even just merge the two routers with
#[derive(Clone)]
enum MultiplexedService {
Axum(axum::Router),
Tonic(tonic::service::AxumRouter),
}
impl Service<Request<Body>> for MultiplexedService {
...
fn call(&mut self, req: Request<Body>) -> Self::Future {
match self {
MultiplexedService::Axum(router) => Box::pin(router.call(req)),
MultiplexedService::Tonic(router) => {
let future = router.call(req);
Box::pin(async {
let response = future.await;
response.map(|response| response.map(Body::new))
})
}
}
}
} Also, it seems some of the methods from |
let grpc = tonic::transport::Server::builder() | ||
.add_service(reflection_service) | ||
.add_service(GreeterServer::new(GrpcServiceImpl::default())) | ||
.into_router(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm reading the docs correctly, I think you want something like:
let grpc = tonic::transport::Server::builder() | |
.add_service(reflection_service) | |
.add_service(GreeterServer::new(GrpcServiceImpl::default())) | |
.into_router(); | |
let grpc = Routes::default() | |
.add_service(reflection_service) | |
.add_service(GreeterServer::new(GrpcServiceImpl::default())) | |
.into_axum_router(); |
which would avoid the deprecated into_router function. It was deprecated in 0.12.2 so it's pretty recent!
Thank you for this pr! I found it very helpful ❤️
Motivation
Updates the gRPC example with the new release of tonic that uses axum 0.7 and hyper 1. Closes #2736.
Solution
Owing to
tower::steer
, I was able to remove the entire multiplex service inmultiplex_service.rs
file. Additionally, tonic thankfully gives us a method to covert its server into axum routes, so we avoid all type inference issues (since they are the same types!)It should be possible to merge the two routers together directly, but I haven't investigated how to make routing possible. I'm not also sure what will happen to the middleware associated with each router. I chose to keep them separate.