How to Run Axum and Tonic on the Same Port with Routing
Running Axum and Tonic on the same server can enhance your application by allowing efficient communication through a single port. This article will guide you through configuring Axum and Tonic to run on the same port, enabling header-based routing to determine the correct service based on request type (HTTP or gRPC). Why Use Axum and Tonic Together? Combining Axum (a web framework) and Tonic (a gRPC framework) in one server is advantageous for microservices architecture. You can efficiently handle both HTTP 1.1 requests and gRPC requests without needing to manage multiple ports. The key is to leverage hyper as the underlying server, which supports multiplexing different protocols. Initial Setup Before diving into the code, ensure both axum and tonic dependencies are included in your Cargo.toml file: [dependencies] tonic = "0.12.3" axum = { version = "0.8.1", features = ["macros"] } hyper = "0.14" tokio = { version = "1.0", features = ["full"] } Multiplexing Axum and Tonic To create a single server that can respond to both HTTP and gRPC requests, we need to: Create an instance of hyper::Server. Route requests based on the content type of incoming headers. Start the server and handle shutdown conditions gracefully. Step 1: Create Your Axum Router Define your Axum router as you normally would. For example: let app = axum::Router::new() .route("/api", axum::routing::get(your_http_handler)); Step 2: Create Your Tonic Service Similarly, define your Tonic gRPC service: let greeter_service = grpc::hello_world::MyGreeter::default(); let grpc_service = Server::builder().add_service(greeter_server::GreeterServer::new(greeter_service)); Step 3: Implement a Multiplexing Logic Based on Headers With both services defined, implement a function to determine how to route the requests: async fn serve_multiplex(req: Request, axum_app: &Router, grpc_service: &YourGrpcService) -> Result { if req.headers().get("content-type") == Some(&HeaderValue::from_static("application/grpc")) { // Forward request to Tonic gRPC service // Convert request to gRPC message and serve with grpc_service } else { // Forward request to Axum axum_app.call(req).await } } Step 4: Start the Server You will now start the server using the hyper implementation: use hyper::{Body, Request, Response, Server}; #[tokio::main] async fn main() -> Result { let addr = ([127, 0, 0, 1], 3000).into(); let make_svc = make_service_fn(|_conn| async { Ok::(service_fn(\|req| serve_multiplex(req, &app, &grpc_service)\)) }); let server = Server::bind(&addr).serve(make_svc); println!("Listening on http://{:?}", addr); server.await?; Ok(()) } Step 5: Graceful Shutdown Ensure you handle graceful shutdown by listening for termination signals. You can set up your server to listen for Ctrl+C signals to shut down each service gracefully. Frequently Asked Questions Can I run Axum and Tonic on different ports? Yes, typical setup involves running them on different ports, but for efficiency, integrating them under one process is preferred. Is there a performance gain by multiplexing services? Yes, reducing the number of open connections can improve resource utilization and response times due to fewer context switches. What happens if I receive a request that doesn't match either service? A well-designed server should always have a fallback mechanism to return an appropriate HTTP response (e.g., 404 for not found). Conclusion By following the above steps, you can efficiently multiplex Axum and Tonic on the same port. This combines the power of both HTTP and gRPC services within a single application context, optimizing resource usage and user experience. Now, you'll be able to handle requests seamlessly based on headers, sending them to the appropriate service with minimal performance penalties.

Running Axum and Tonic on the same server can enhance your application by allowing efficient communication through a single port. This article will guide you through configuring Axum and Tonic to run on the same port, enabling header-based routing to determine the correct service based on request type (HTTP or gRPC).
Why Use Axum and Tonic Together?
Combining Axum (a web framework) and Tonic (a gRPC framework) in one server is advantageous for microservices architecture. You can efficiently handle both HTTP 1.1 requests and gRPC requests without needing to manage multiple ports. The key is to leverage hyper
as the underlying server, which supports multiplexing different protocols.
Initial Setup
Before diving into the code, ensure both axum
and tonic
dependencies are included in your Cargo.toml
file:
[dependencies]
tonic = "0.12.3"
axum = { version = "0.8.1", features = ["macros"] }
hyper = "0.14"
tokio = { version = "1.0", features = ["full"] }
Multiplexing Axum and Tonic
To create a single server that can respond to both HTTP and gRPC requests, we need to:
- Create an instance of
hyper::Server
. - Route requests based on the content type of incoming headers.
- Start the server and handle shutdown conditions gracefully.
Step 1: Create Your Axum Router
Define your Axum router as you normally would. For example:
let app = axum::Router::new()
.route("/api", axum::routing::get(your_http_handler));
Step 2: Create Your Tonic Service
Similarly, define your Tonic gRPC service:
let greeter_service = grpc::hello_world::MyGreeter::default();
let grpc_service =
Server::builder().add_service(greeter_server::GreeterServer::new(greeter_service));
Step 3: Implement a Multiplexing Logic Based on Headers
With both services defined, implement a function to determine how to route the requests:
async fn serve_multiplex(req: Request,
axum_app: &Router,
grpc_service: &YourGrpcService) -> Result, Infallible> {
if req.headers().get("content-type") == Some(&HeaderValue::from_static("application/grpc")) {
// Forward request to Tonic gRPC service
// Convert request to gRPC message and serve with grpc_service
} else {
// Forward request to Axum
axum_app.call(req).await
}
}
Step 4: Start the Server
You will now start the server using the hyper
implementation:
use hyper::{Body, Request, Response, Server};
#[tokio::main]
async fn main() -> Result<(), Box> {
let addr = ([127, 0, 0, 1], 3000).into();
let make_svc =
make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(\|req| serve_multiplex(req, &app, &grpc_service)\)) });
let server = Server::bind(&addr).serve(make_svc);
println!("Listening on http://{:?}", addr);
server.await?;
Ok(())
}
Step 5: Graceful Shutdown
Ensure you handle graceful shutdown by listening for termination signals. You can set up your server to listen for Ctrl+C signals to shut down each service gracefully.
Frequently Asked Questions
Can I run Axum and Tonic on different ports?
Yes, typical setup involves running them on different ports, but for efficiency, integrating them under one process is preferred.
Is there a performance gain by multiplexing services?
Yes, reducing the number of open connections can improve resource utilization and response times due to fewer context switches.
What happens if I receive a request that doesn't match either service?
A well-designed server should always have a fallback mechanism to return an appropriate HTTP response (e.g., 404 for not found).
Conclusion
By following the above steps, you can efficiently multiplex Axum and Tonic on the same port. This combines the power of both HTTP and gRPC services within a single application context, optimizing resource usage and user experience. Now, you'll be able to handle requests seamlessly based on headers, sending them to the appropriate service with minimal performance penalties.