gRPC server and client support for the Armature framework.
- Tonic Integration - Built on the Tonic gRPC library
- Protobuf compilation - Not performed by this crate. Depend on
tonic-builddirectly in your own crate'sbuild.rsto generate service code from.protofiles, then use the generated types together witharmature-grpc's server/client/interceptor helpers. - Streaming - Unary, server, client, and bidirectional streaming
- Interceptors - Standalone request/response helpers (logging, metrics,
request IDs, auth). Except for
AuthInterceptor::server_interceptor(wired in via tonic'sInterceptedService/with_interceptor), these are not an automatically-applied pipeline — callInterceptor::interceptyourself where you want them to run. - TLS - Secure client and server connections with rustls (
tonic'stls-ringfeature), configured viaGrpcClientTlsConfig/GrpcServerTlsConfig - Retry - Opt-in:
GrpcClientConfig::retry_enabled/max_retry_attemptsdo nothing on their own. Retry only happens for calls explicitly wrapped inGrpcChannel::call_with_retry(see below) — there is no automatic retry. - Compression - gzip/zstd, applied via
CompressionMiddleware::wrap_server(server) /CompressionMiddleware::wrap_channel(client)
[dependencies]
armature-grpc = "0.2"use armature_grpc::{GrpcServer, GrpcServerConfig, Request, Response, Status};
pub struct MyService;
#[tonic::async_trait]
impl Greeter for MyService {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> {
let reply = HelloReply {
message: format!("Hello {}!", request.into_inner().name),
};
Ok(Response::new(reply))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = GrpcServerConfig::builder()
.bind_address("0.0.0.0:50051")
.build()?;
GrpcServer::builder(config)
.serve(GreeterServer::new(MyService))
.await?;
Ok(())
}use armature_grpc::{GrpcClient, GrpcClientConfig};
let config = GrpcClientConfig::builder()
.endpoint("http://localhost:50051")
.build();
let channel = GrpcClient::connect(config).await?;
let mut client = GreeterClient::new(channel.inner().clone());
let response = client.say_hello(HelloRequest { name: "World".into() }).await?;
println!("Response: {}", response.into_inner().message);retry_enabled / max_retry_attempts on GrpcClientConfig are not applied
automatically. To actually retry, wrap each call in GrpcChannel::call_with_retry:
let config = GrpcClientConfig::builder()
.endpoint("http://localhost:50051")
.retry(true)
.max_retry_attempts(3)
.build();
let channel = GrpcClient::connect(config).await?;
let response = channel
.call_with_retry(|| {
let mut client = GreeterClient::new(channel.inner().clone());
let request = HelloRequest { name: "World".into() };
async move { client.say_hello(request).await }
})
.await?;MIT OR Apache-2.0