핵심 요약
tonic 0.13이 2026년 3월 출시, axum 0.8과 라우터 통합이 안정. 하나의 hyper 1.x 서버에 gRPC, HTTP, 헬스체크, 메트릭을 같은 포트로 묶을 수 있다. tower 미들웨어 생태계를 그대로 공유해 인증·재시도·타임아웃 코드가 사라진다. Go gRPC 대비 동등 워크로드에서 메모리 1/4, p99 30% 감소.
1. 왜 axum 통합인가
이전 버전은 tonic이 자체 서버를 띄워 8080은 gRPC, 8081은 HTTP로 포트가 갈렸다. 0.13은 axum::Router::merge로 한 포트에 통합. 사이드카, 로드밸런서, k8s Service 정의가 간단해진다.
2. 기본 구성
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_prost_build::configure()
.build_server(true)
.compile_protos(&["proto/order.proto"], &["proto"])?;
Ok(())
}
3. axum과 같은 라우터에
use axum::{Router, routing::get};
use tonic::transport::Server;
let grpc = OrderServiceServer::new(OrderSvc::default());
let app = Router::new()
.route("/healthz", get(|| async { "ok" }))
.route("/metrics", get(metrics_handler))
.merge(Router::from(grpc));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;
From<Server> for Router 임플이 0.13의 킬러 피처.
4. tower 미들웨어
use tower::ServiceBuilder;
use tower_http::trace::TraceLayer;
let middleware = ServiceBuilder::new()
.layer(TraceLayer::new_for_grpc())
.timeout(Duration::from_secs(30))
.concurrency_limit(1024)
.layer(AuthLayer::new(jwt_verifier));
let grpc = OrderServiceServer::new(svc)
.max_decoding_message_size(8 * 1024 * 1024);
let app = Router::new()
.merge(Router::from(grpc))
.layer(middleware);
5. 스트리밍 — bi-directional
type EventStream = Pin<Box<dyn Stream<Item = Result<Event, Status>> + Send>>;
async fn stream_events(
&self,
req: Request<Streaming<Subscribe>>,
) -> Result<Response<Self::StreamEventsStream>, Status> {
let mut inbound = req.into_inner();
let (tx, rx) = mpsc::channel(128);
tokio::spawn(async move {
while let Some(Ok(sub)) = inbound.next().await {
// publish events for sub.topics
}
});
Ok(Response::new(Box::pin(ReceiverStream::new(rx))))
}
6. 실측 — Go grpc-go vs tonic 0.13
| 지표 | grpc-go 1.65 | tonic 0.13 |
|---|---|---|
| 처리량 | 48,000 rps | 192,000 rps |
| p50 지연 | 2.1ms | 0.6ms |
| p99 지연 | 14ms | 3.8ms |
| RSS 메모리 | 420MB | 110MB |
| CPU 사용률 | 92% | 74% |
7. 관측 — OpenTelemetry
use opentelemetry_otlp::WithExportConfig;
let provider = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(opentelemetry_otlp::new_exporter().tonic())
.install_batch(opentelemetry_sdk::runtime::Tokio)?;
let app = Router::new()
.merge(Router::from(grpc))
.layer(OtelInLayer::default());
8. 의사결정
인 메모리 캐시 + 단순 라우팅이 핵심인 fan-out 게이트웨이, 거대 토픽 fan-in 스트리밍, 사이드카 프록시. 비즈니스 로직이 무겁고 팀이 Go에 능숙하면 굳이 옮길 이유 없다. 새 인프라 서비스를 시작하는 자리라면 tonic 0.13은 진지한 후보. axum 통합이 운영 단순화에 결정적이다.
참고
- github.com/hyperium/tonic
- docs.rs/tonic/0.13
- github.com/tokio-rs/axum

댓글 0