Tonic supports four types of RPC patterns. All service methods receive a tonic::Request<T> and return a Result<tonic::Response<T>, tonic::Status>. The type of T depends on your .proto definition:
- Simple RPC: Receives a single value and returns a single value.
- Server-side streaming RPC: Receives a single value and returns a
Stream of values. You can use tokio::sync::mpsc and tokio_stream::wrappers::ReceiverStream to implement this. - Client-side streaming RPC: Receives a
tonic::Streaming<T> and returns a single value. You can iterate over the stream using StreamExt::next(). - Bidirectional streaming RPC: Receives a
tonic::Streaming<T> and returns a Stream of values. The async_stream crate is useful for yielding values from an asynchronous transformation.
// Simple RPC
async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
Ok(Response::new(feature))
}
// Server-side streaming
type ListFeaturesStream = ReceiverStream<Result<Feature, Status>>;
async fn list_features(&self, request: Request<Rectangle>) -> Result<Response<Self::ListFeaturesStream>, Status> {
let (tx, rx) = mpsc::channel(4);
// ... spawn task to send items to tx
Ok(Response::new(ReceiverStream::new(rx)))
}
// Client-side streaming
async fn record_route(&self, request: Request<tonic::Streaming<Point>>) -> Result<Response<RouteSummary>, Status> {
let mut stream = request.into_inner();
while let Some(point) = stream.next().await {
let point = point?;
// ... process point
}
Ok(Response::new(summary))
}
// Bidirectional streaming
type RouteChatStream = Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>;
async fn route_chat(&self, request: Request<tonic::Streaming<RouteNote>>) -> Result<Response<Self::RouteChatStream>, Status> {
let mut stream = request.into_inner();
let output = async_stream::try_stream! {
while let Some(note) = stream.next().await {
let note = note?;
yield note;
}
};
Ok(Response::new(Box::pin(output) as Self::RouteChatStream))
}