gRPC com Go (introdução)
LidoEsta aula introduz o gRPC em Go, explicando seus conceitos fundamentais, a definição de serviços com Protobuf, a implementação de clientes e servidores, e os cenários ideais para seu uso. O aluno aprenderá a criar um serviço gRPC simples, compreendendo a geração de código e a comunicação eficiente entre sistemas.
O gRPC é um framework de chamada de procedimento remoto (RPC) de alto desempenho desenvolvido pelo Google. Ele permite que programas em diferentes linguagens se comuniquem de forma eficiente, usando HTTP/2 como transporte e Protocol Buffers (Protobuf) como linguagem de definição de interface (IDL) e formato de serialização. Nesta aula, vamos explorar os fundamentos do gRPC no contexto da linguagem Go, desde os conceitos básicos até a implementação prática de um servidor e um cliente.
Diferente de APIs REST tradicionais baseadas em JSON e HTTP/1.1, o gRPC oferece vantagens como multiplexação de streams, comunicação bidirecional e serialização binária compacta, tornando-o ideal para microsserviços e sistemas distribuídos. Com o suporte nativo do Go, você pode criar serviços robustos e escaláveis com relativa facilidade.
O que é gRPC
gRPC é um framework de RPC de código aberto que usa HTTP/2 para transporte e Protocol Buffers para serialização de dados. Ele foi projetado para ser rápido, eficiente e independente de linguagem, permitindo que serviços escritos em Go, Java, Python, etc., se comuniquem de forma transparente. Com gRPC, você define um serviço com métodos e tipos de mensagem em um arquivo .proto, e o gRPC gera código cliente e servidor automaticamente.
O gRPC suporta quatro tipos de comunicação: unária (request-response), streaming de servidor, streaming de cliente e streaming bidirecional. Isso dá flexibilidade para diferentes padrões de comunicação, como envio de eventos ou troca contínua de dados. Além disso, o uso de HTTP/2 permite multiplexação de múltiplas chamadas em uma única conexão, reduzindo latência e melhorando o desempenho.
Exemplo de definição de um serviço simples em um arquivo .proto:
syntax = "proto3";
package exemplo;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}Esse arquivo define um serviço com um método unário que recebe um HelloRequest e retorna um HelloReply. A partir dele, geramos código Go para servidor e cliente.
Protobuf (visão geral)
Protocol Buffers (Protobuf) é um mecanismo de serialização de dados estruturados, desenvolvido pelo Google, que é compacto e eficiente. Ele é usado como IDL no gRPC para definir a estrutura das mensagens e os contratos dos serviços. Protobuf usa um formato binário, o que o torna menor e mais rápido de serializar/deserializar em comparação com JSON ou XML.
Para usar Protobuf em Go, é necessário instalar o compilador protoc e o plugin protoc-gen-go, que gera código Go a partir dos arquivos .proto. O código gerado inclui tipos de dados para as mensagens e interfaces para os serviços. Essa geração automática garante que cliente e servidor estejam sempre em sincronia com a definição.
Exemplo de instalação das ferramentas:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latestDepois, para gerar o código Go, execute:
protoc --go_out=. --go-grpc_out=. exemplo.protoIsso gerará dois arquivos: exemplo.pb.go (mensagens) e exemplo_grpc.pb.go (interfaces de serviço).
Cliente e servidor
No gRPC, o servidor implementa a interface definida no arquivo .proto e expõe um endpoint HTTP/2. O cliente cria uma conexão com esse endpoint e chama os métodos como se fossem locais. Em Go, usamos o pacote google.golang.org/grpc para criar tanto o servidor quanto o cliente.
Servidor: Criamos um tipo que implementa a interface gerada e registramos no servidor gRPC. O servidor escuta em uma porta TCP e usa o pacote grpc.NewServer().
Exemplo de implementação de servidor para o serviço Greeter:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "caminho/para/proto"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Olá, " + req.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("falha ao escutar: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Println("Servidor gRPC ouvindo em :50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("falha ao servir: %v", err)
}
}Cliente: Criamos uma conexão gRPC com o endereço do servidor e instanciamos o cliente a partir da stub gerada. Depois, chamamos os métodos da mesma forma que uma função local.
Exemplo de cliente:
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "caminho/para/proto"
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("não foi possível conectar: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := c.SayHello(ctx, &pb.HelloRequest{Name: "Mundo"})
if err != nil {
log.Fatalf("erro ao chamar SayHello: %v", err)
}
log.Printf("Resposta: %s", resp.Message)
}Note que usamos grpc.WithInsecure() para conexões sem TLS, mas em produção é recomendado usar credenciais seguras.
Quando usar
O gRPC é particularmente útil em ambientes de microsserviços, onde a comunicação entre serviços precisa ser rápida e confiável. Ele também é excelente para cenários que exigem streaming de dados, como feeds de eventos, telemetria ou bate-papo em tempo real. Além disso, por ser baseado em HTTP/2, oferece melhor desempenho e menor latência em comparação com REST/JSON.
No entanto, gRPC não é ideal para todos os casos. Se você precisa de comunicação com navegadores web, onde o suporte a HTTP/2 e Protobuf não é nativo, ou se exige simplicidade e compatibilidade ampla, REST pode ser mais adequado. Além disso, o gRPC pode ter uma curva de aprendizado maior e a depuração de payloads binários é mais difícil.
Em resumo, use gRPC quando você tem controle sobre ambos os lados da comunicação (cliente e servidor), precisa de alta performance, streaming ou quando está construindo uma arquitetura de microsserviços. Avalie os trade-offs antes de decidir.
Boas práticas
- Sempre defina versões de API no seu arquivo .proto, por exemplo, usando pacotes com sufixo v1, v2, etc.
- Utilize contextos com timeouts para evitar chamadas bloqueantes.
- Em produção, configure credenciais TLS para comunicação segura.
- Organize seus arquivos .proto em um diretório separado e compartilhe entre clientes e servidores.
- Considere usar interceptors para logging, autenticação ou métricas.
Referências
- Documentação oficial do gRPC em Go
- Documentação do Protocol Buffers
- Pacote grpc no pkg.go.dev
- Tutorial de Protocol Buffers em Go
- Introdução ao gRPC
- Repositório do gRPC-Go no GitHub
Exercícios
Explique a diferença entre gRPC e REST em termos de transporte e serialização.
✓ Resposta: O gRPC usa HTTP/2 como transporte e serializa dados em formato binário (Protobuf), enquanto REST normalmente usa HTTP/1.1 e JSON (texto). Isso torna o gRPC mais rápido e eficiente, além de suportar streaming bidirecional, mas REST é mais simples e compatível com navegadores.Escreva um arquivo .proto que defina um serviço chamado 'Calculator' com um método 'Add' que recebe dois números inteiros e retorna a soma.
✓ Resposta:syntax = "proto3"; package calc; service Calculator { rpc Add (AddRequest) returns (AddReply); } message AddRequest { int32 a = 1; int32 b = 2; } message AddReply { int32 result = 1; }Qual a finalidade do comando 'protoc --go_out=. --go-grpc_out=. exemplo.proto'?
✓ Resposta: Esse comando gera o código Go a partir do arquivo exemplo.proto. O primeiro --go_out gera o código das mensagens (estruturas de dados) e o --go-grpc_out gera o código das interfaces de serviço (stubs para cliente e servidor).Cite duas situações em que gRPC é mais adequado que REST.
✓ Resposta: 1) Comunicação entre microsserviços que exige alta performance e baixa latência. 2) Cenários que precisam de streaming de dados, como envio de logs em tempo real ou feeds de eventos.No exemplo de servidor, qual é o papel do método 'RegisterGreeterServer'?
✓ Resposta: Esse método registra a implementação do serviço no servidor gRPC, associando o tipo 'server' à interface GreeterServer. Isso permite que o servidor saiba como lidar com as chamadas recebidas para o serviço Greeter.