1. Introdução
Neste codelab, você vai usar o gRPC-Rust para criar um cliente e um servidor que formam a base de um aplicativo de mapeamento de rotas escrito em Rust.
Ao final do tutorial, você terá um cliente que se conecta a um servidor remoto usando a implementação oficial do protocolo gRPC em Rust para buscar o nome ou o endereço postal de um local em coordenadas específicas em um mapa. Um aplicativo completo pode usar esse design de cliente-servidor para enumerar ou resumir pontos de interesse ao longo de uma rota.
O serviço é definido em um arquivo de buffers de protocolo, que será usado para gerar código boilerplate para o cliente e o servidor, para que eles possam se comunicar entre si, economizando tempo e esforço na implementação dessa funcionalidade.
Esse código gerado cuida não apenas das complexidades da comunicação entre o servidor e o cliente, mas também da serialização e desserialização de dados.
O que você vai aprender
- Como usar buffers de protocolo para definir uma API de serviço.
- Como criar um cliente baseado em gRPC a partir de uma definição de buffer de protocolo usando a geração de código automatizada.
- Uma compreensão da comunicação cliente-servidor com gRPC.
Este codelab é destinado a desenvolvedores de Rust que não conhecem o gRPC ou que querem se atualizar sobre o gRPC, ou qualquer outra pessoa interessada em criar sistemas distribuídos. Não é necessário ter experiência anterior com o gRPC.
2. Antes de começar
Pré-requisitos
Instale o seguinte:
- GCC. Siga as instruções aqui.
- Git: instruções de instalação aqui.
- Rust, versão 1.88.0. Siga as instruções de instalação aqui.
Acessar o código
Para que você não precise começar do zero, este codelab fornece um scaffold do código-fonte do aplicativo para você concluir. As etapas a seguir mostram como concluir o aplicativo, incluindo o uso dos plug-ins do compilador de buffer de protocolo para gerar o código gRPC boilerplate.
Primeiro, crie o diretório de trabalho do codelab e cd nele:
mkdir grpc-rust-getting-started && cd grpc-rust-getting-started
Faça o download e extraia o codelab:
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/2026.tar.gz \
| tar xvz --strip-components=4 \
grpc-codelabs-2026/codelabs/grpc-rust-getting-started/start_here
Como alternativa, você pode fazer o download do arquivo .zip que contém apenas o diretório do codelab e descompactá-lo manualmente.
O código-fonte concluído está disponível no GitHub se você quiser pular a digitação de uma implementação.
3. Definir o serviço
A primeira etapa é definir o serviço gRPC do aplicativo, o método RPC e os tipos de mensagem de solicitação e resposta usando buffers de protocolo. Seu serviço vai fornecer:
- Um método RPC chamado
GetFeatureque o servidor implementa e o cliente chama. - Os tipos de mensagem
PointeFeature, que são estruturas de dados trocadas entre o cliente e o servidor ao usar o métodoGetFeature. O cliente fornece coordenadas de mapa como umPointna solicitaçãoGetFeatureao servidor, e o servidor responde com umFeaturecorrespondente que descreve o que está localizado nessas coordenadas.
Esse método RPC e os tipos de mensagem serão definidos no arquivo proto/routeguide.proto do código-fonte fornecido.
Os buffers de protocolo são conhecidos como protobufs. Para mais informações sobre a terminologia do gRPC, consulte Conceitos básicos, arquitetura e ciclo de vida do gRPC.
Método de serviço
Vamos primeiro definir nossos métodos de serviço e, em seguida, definir os tipos de mensagem Point e Feature. O arquivo proto/routeguide.proto tem uma estrutura service chamada RouteGuide que define um ou mais métodos fornecidos pelo serviço do aplicativo.
Adicione o método rpc GetFeature dentro da definição RouteGuide. Como explicado anteriormente, esse método vai procurar o nome ou o endereço de um local em um determinado conjunto de coordenadas. Portanto, faça com que GetFeature retorne um Feature para um determinado Point:
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
Esse é um método RPC unário: um RPC simples em que o cliente envia uma solicitação ao servidor e aguarda uma resposta, assim como uma chamada de função local.
Tipos de mensagem
No arquivo proto/routeguide.proto do código-fonte, defina primeiro o tipo de mensagem Point. Um Point representa um par de coordenadas de latitude e longitude em um mapa. Para este codelab, use números inteiros para as coordenadas:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
Os números 1 e 2 são números de ID exclusivos para cada um dos campos na estrutura message.
Em seguida, defina o tipo de mensagem Feature. Um Feature usa um campo string para o nome ou endereço postal de algo em um local especificado por um Point:
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
4. Gerar o código do cliente e do servidor
Já fornecemos o código gerado do arquivo .proto no diretório generated/, incluindo todas as adições feitas acima. No entanto, gostaríamos de explicar como a geração de código funciona.
Nosso arquivo .proto descreve todas as structs e funções usadas por um cliente ou servidor. Usamos um script de build do Cargo (build.rs) com o crate grpc-protobuf-build para gerar esse código automaticamente.
Em Cargo.toml, adicionamos grpc-protobuf-build em [build-dependencies].
Em build.rs, configuramos grpc_protobuf_build::CodeGen para compilar proto/routeguide.proto no diretório generated/. As linhas principais estão aqui:
grpc_protobuf_build::CodeGen::new()
.include("proto")
.input("routeguide.proto")
.output_dir("generated")
.compile()
.unwrap();
Isso chama a geração de código do crate grpc_protobuf_build, transmitindo o routeguide.proto. Envolvemos isso em algum código para ser executado apenas quando uma flag de recurso é transmitida, para que ela seja regenerada apenas quando você quiser. Não é necessário executar isso agora, já que geramos o código para você.
cargo build --bin routeguide-server --features regenerate_proto
Quando você executa o build do Cargo, build.rs compila as definições de buffer de protocolo no diretório generate/, incluindo:
- Definições de struct para tipos de mensagem
PointeFeature. - Um trait de serviço do Tonic que precisaremos implementar para o servidor:
route_guide_server::RouteGuide. - Um tipo de cliente gRPC-Rust que usaremos para chamar o servidor:
route_guide_client::RouteGuideClient<T>.
Consulte o guia protoc-gen-rust-grpc para mais informações.
Em seguida, vamos implementar os métodos de serviço no servidor.
5. Implementar o serviço
Em src/server/server.rs, podemos colocar o código gerado no escopo usando a macro include_generated_proto! do gRPC e importar o trait RouteGuide e Point.
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
use grpc_pb::{
route_guide_server::{RouteGuideServer, RouteGuide},
Point, Feature,
};
Podemos começar definindo uma struct para representar nosso serviço. Podemos fazer isso em src/server/server.rs por enquanto:
#[derive(Debug)]
pub struct RouteGuideService {
features: Vec<Feature>,
}
Agora, precisamos implementar o trait route_guide_server::RouteGuide do nosso código gerado.
RPC unário simples
O RouteGuideService implementa todos os nossos métodos de serviço. A função get_feature no lado do servidor é onde o trabalho principal é feito: ela recebe uma mensagem Point do cliente e retorna em uma mensagem Feature as informações de local correspondentes de uma lista de lugares conhecidos. Confira a implementação da função em src/server/server.rs:
#[tonic::async_trait]
impl RouteGuide for RouteGuideService {
async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
println!("GetFeature = {:?}", request);
let requested_point = request.get_ref();
for feature in self.features.iter() {
if feature.location().latitude() == requested_point.latitude() {
if feature.location().longitude() == requested_point.longitude(){
return Ok(Response::new(feature.clone()))
};
};
}
Ok(Response::new(Feature::default()))
}
}
Depois de implementar esse método, também precisamos iniciar um servidor gRPC para que os clientes possam usar nosso serviço. Substitua main() por este.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:10000".parse().unwrap();
println!("RouteGuideServer listening on: {addr}");
let route_guide = RouteGuideService {
features: load(),
};
let svc = RouteGuideServer::new(route_guide);
Server::builder().add_service(svc).serve(addr).await?;
Ok(())
}
Confira o que está acontecendo em main(), etapa por etapa:
- Especifique a porta que queremos usar para detectar solicitações de clientes.
- Crie um
RouteGuideServicecom recursos carregados chamando a função auxiliarload(). - Crie uma instância do servidor gRPC usando
RouteGuideServer::new()com o serviço que criamos. - Registre nossa implementação de serviço com o servidor gRPC.
- Chame
serve()no servidor com os detalhes da porta para fazer uma espera de bloqueio até que o processo seja encerrado.
6. Criar o cliente
Nesta seção, vamos analisar a criação de um cliente Rust para nosso serviço RouteGuide em src/client/client.rs.
Como fizemos em src/server/server.rs, podemos colocar o código gerado no escopo usando a macro include_generated_proto! do gRPC e importar o tipo RouteGuideClient.
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
use grpc_pb::{
route_guide_client::RouteGuideClient,
Point,
};
Chamar métodos de serviço
No gRPC-Rust, os RPCs são assíncronos e não bloqueadores, usando a sintaxe async/await do Rust para aguardar respostas do servidor.
Para chamar métodos de serviço, primeiro criamos um Channel usando Channel::builder(), especificando o endereço do servidor (dns:///[::1]:10000) e as credenciais de conexão (LocalChannelCredentials). Em seguida, transmitimos o canal para RouteGuideClient::new() para instanciar nosso cliente:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new gRPC channel
let channel = Channel::builder(
"dns:///[::1]:10000",
Arc::new(LocalChannelCredentials::new()),
)
.build();
// Create a new client
let client = RouteGuideClient::new(channel);
}
Nessa função, RouteGuideClient::new() vincula o canal genérico ao stub do cliente gerado que implementa os métodos definidos na nossa definição de serviço .proto.
RPC simples
Chamar o RPC simples GetFeature é tão simples quanto chamar um método local. Adicione isso em main():
println!("*** SIMPLE RPC ***");
let point = proto!(Point {
latitude: 409_146_138,
longitude: -746_188_906,
});
let response = client
.get_feature(point)
.await
.expect("RPC error");
No gRPC-Rust, transmitimos a mensagem protobuf Point diretamente para client.get_feature(point). Aguardar o futuro retornado produz a resposta Feature diretamente, sem precisar de mais chamadas de método.
Em seguida, imprima os campos da resposta:
println!(
"Response = Name = \"{}\", Latitude = {}, Longitude = {}",
response.name(),
response.location().latitude(),
response.location().longitude()
);
No total, a função main() do cliente deve ser assim:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Create a new gRPC channel
let channel = Channel::builder(
"dns:///[::1]:10000",
Arc::new(LocalChannelCredentials::new()),
)
.build();
// 2. Instantiate the generated RouteGuideClient
let client = RouteGuideClient::new(channel);
println!("*** SIMPLE RPC ***");
let point = proto!(Point {
latitude: 409_146_138,
longitude: -746_188_906,
});
let response = client
.get_feature(point)
.await
.expect("RPC error");
println!(
"Response = Name = \"{}\", Latitude = {}, Longitude = {}",
response.name(),
response.location().latitude(),
response.location().longitude()
);
Ok(())
}
7. Faça um teste
Para executar o cliente e o servidor, primeiro verifique se os dois destinos binários estão definidos em Cargo.toml:
[[bin]]
name = "routeguide-server"
path = "src/server/server.rs"
[[bin]]
name = "routeguide-client"
path = "src/client/client.rs"
Em seguida, execute os seguintes comandos no nosso diretório de trabalho:
- Execute o servidor em um terminal:
cargo run --bin routeguide-server
- Execute o cliente em outro terminal:
cargo run --bin routeguide-client
Você verá uma saída como esta, com carimbos de data/hora omitidos para fins de esclarecimento:
*** SIMPLE RPC ***
Response = Name = "Berkshire Valley Management Area Trail, Jefferson, NJ, USA", Latitude = 409146138, Longitude = -746188906
8. A seguir
- Continue com o codelab Introdução ao gRPC-Rust (streaming).
- Confira o repositório oficial do gRPC-Rust.
- Saiba mais sobre a arquitetura do gRPC em Conceitos básicos.
- Consulte a documentação do gRPC-Rust em gRPC.io.
9. Colaboradores deste codelab
- Cathy Zhao
- Lucio Franco
- Arvind Bright
- Nathaniel Ford