Go

Guia Completo de gRPC em Go: Protocol Buffers, Streaming e Interceptors

21 min de leitura

Guia Completo de gRPC em Go: Protocol Buffers, Streaming e Interceptors

O que é gRPC e Por Que Importa gRPC é um framework de chamada de procedimento remoto (RPC) moderno, desenvolvido pelo Google, que permite que aplicações se comuniquem de forma eficiente através da rede. Diferentemente de REST, que usa HTTP/1.1 e serialização JSON, gRPC é construído sobre HTTP/2 e utiliza Protocol Buffers para serialização de dados. Essa combinação resulta em latência menor, melhor utilização de banda e suporte nativo para streaming bidirecional. Em Go, gRPC é particularmente poderoso porque a linguagem foi projetada para lidar com concorrência de forma simples e elegante. Se você trabalha com microsserviços, APIs internas de alta performance ou sistemas que precisam comunicação em tempo real, gRPC é uma escolha técnica que justifica o investimento em aprendizado. A comunidade Go ao redor de gRPC é ativa, bem documentada e as bibliotecas disponíveis são maduras. Protocol Buffers: A Base de Tudo O que São Protocol Buffers Protocol Buffers (protobuf) é uma linguagem de serialização independente de linguagem

<h2>O que é gRPC e Por Que Importa</h2>

<p>gRPC é um framework de chamada de procedimento remoto (RPC) moderno, desenvolvido pelo Google, que permite que aplicações se comuniquem de forma eficiente através da rede. Diferentemente de REST, que usa HTTP/1.1 e serialização JSON, gRPC é construído sobre HTTP/2 e utiliza Protocol Buffers para serialização de dados. Essa combinação resulta em latência menor, melhor utilização de banda e suporte nativo para streaming bidirecional.</p>

<p>Em Go, gRPC é particularmente poderoso porque a linguagem foi projetada para lidar com concorrência de forma simples e elegante. Se você trabalha com microsserviços, APIs internas de alta performance ou sistemas que precisam comunicação em tempo real, gRPC é uma escolha técnica que justifica o investimento em aprendizado. A comunidade Go ao redor de gRPC é ativa, bem documentada e as bibliotecas disponíveis são maduras.</p>

<h2>Protocol Buffers: A Base de Tudo</h2>

<h3>O que São Protocol Buffers</h3>

<p>Protocol Buffers (protobuf) é uma linguagem de serialização independente de linguagem desenvolvida pelo Google. Você define suas estruturas de dados em arquivos <code>.proto</code> e ferramentas geram código para serialização e desserialização em qualquer linguagem. Ao contrário de JSON, os Protocol Buffers são binários, mais compactos e mais rápidos de processar.</p>

<p>Um arquivo <code>.proto</code> é a interface contratual entre cliente e servidor. Quando você modifica um <code>.proto</code>, precisa regenerar o código em ambos os lados. Essa abordagem garante compatibilidade, versionamento claro e documentação automática.</p>

<h3>Estrutura Básica de um Arquivo Proto</h3>

<p>Vamos criar um arquivo <code>user.proto</code> para um serviço simples de gerenciamento de usuários:</p>

<pre><code class="language-protobuf">syntax = &quot;proto3&quot;;

package user;

option go_package = &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;;

message User {

int32 id = 1;

string name = 2;

string email = 3;

int32 age = 4;

}

message CreateUserRequest {

string name = 1;

string email = 2;

int32 age = 3;

}

message CreateUserResponse {

User user = 1;

string message = 2;

}

service UserService {

rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);

rpc GetUser(User) returns (User);

}</code></pre>

<p>A declaração <code>syntax = &quot;proto3&quot;</code> define a versão do Protocol Buffers (a mais atual e recomendada). O <code>package</code> organiza seu código gerado, e <code>go_package</code> especifica o caminho Go do pacote gerado. Cada campo tem um número único (<code>= 1</code>, <code>= 2</code>, etc.) que identifica aquele campo de forma binária — nunca reutilize esses números em versões futuras, pois isso quebra compatibilidade.</p>

<h3>Gerando Código Go a Partir do Proto</h3>

<p>Para converter seu arquivo <code>.proto</code> em código Go, você precisa instalar o compilador <code>protoc</code> e plugins Go:</p>

<pre><code class="language-bash"># Instalar protoc (no macOS com Homebrew)

brew install protobuf

Instalar plugins Go para protoc

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Gerar código Go

protoc --go_out=. --go-grpc_out=. user.proto</code></pre>

<p>Isso criará dois arquivos: <code>user.pb.go</code> (estruturas de dados) e <code>user_grpc.pb.go</code> (interfaces e clientes/servidores gRPC). Nunca edite esses arquivos manualmente — eles são gerados e serão sobrescritos na próxima execução do <code>protoc</code>.</p>

<h2>Implementando um Serviço gRPC Básico</h2>

<h3>Servidor gRPC</h3>

<p>Agora vamos implementar um servidor que satisfaça a interface gerada. Crie um arquivo <code>server.go</code>:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;net&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

type userServer struct {

pb.UnimplementedUserServiceServer

users map[int32]*pb.User

nextID int32

}

func (s userServer) CreateUser(ctx context.Context, req pb.CreateUserRequest) (*pb.CreateUserResponse, error) {

s.nextID++

user := &amp;pb.User{

Id: s.nextID,

Name: req.Name,

Email: req.Email,

Age: req.Age,

}

s.users[user.Id] = user

return &amp;pb.CreateUserResponse{

User: user,

Message: &quot;User created successfully&quot;,

}, nil

}

func (s userServer) GetUser(ctx context.Context, req pb.User) (*pb.User, error) {

user, ok := s.users[req.Id]

if !ok {

return nil, fmt.Errorf(&quot;user not found&quot;)

}

return user, nil

}

func main() {

listener, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)

if err != nil {

log.Fatalf(&quot;Failed to listen: %v&quot;, err)

}

grpcServer := grpc.NewServer()

pb.RegisterUserServiceServer(grpcServer, &amp;userServer{

users: make(map[int32]*pb.User),

nextID: 0,

})

fmt.Println(&quot;Server running on :50051&quot;)

if err := grpcServer.Serve(listener); err != nil {

log.Fatalf(&quot;Failed to serve: %v&quot;, err)

}

}</code></pre>

<p>O servidor escuta na porta 50051 (padrão para gRPC). A estrutura <code>userServer</code> implementa as interfaces geradas pelo <code>protoc</code>. Note que herdamos <code>UnimplementedUserServiceServer</code> para garantir compatibilidade futura se novos métodos forem adicionados ao serviço.</p>

<h3>Cliente gRPC</h3>

<p>Crie um arquivo <code>client.go</code> para testar:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

func main() {

conn, err := grpc.Dial(&quot;localhost:50051&quot;, grpc.WithInsecure())

if err != nil {

log.Fatalf(&quot;Failed to connect: %v&quot;, err)

}

defer conn.Close()

client := pb.NewUserServiceClient(conn)

// Criar um usuário

ctx, cancel := context.WithTimeout(context.Background(), time.Second)

defer cancel()

createResp, err := client.CreateUser(ctx, &amp;pb.CreateUserRequest{

Name: &quot;João Silva&quot;,

Email: &quot;joao@example.com&quot;,

Age: 28,

})

if err != nil {

log.Fatalf(&quot;CreateUser failed: %v&quot;, err)

}

fmt.Printf(&quot;Created user: %v\n&quot;, createResp.User)

// Buscar um usuário

ctx, cancel = context.WithTimeout(context.Background(), time.Second)

defer cancel()

getResp, err := client.GetUser(ctx, &amp;pb.User{Id: createResp.User.Id})

if err != nil {

log.Fatalf(&quot;GetUser failed: %v&quot;, err)

}

fmt.Printf(&quot;Retrieved user: %v\n&quot;, getResp)

}</code></pre>

<p>O cliente cria uma conexão com o servidor usando <code>grpc.Dial</code>. Note o uso de <code>context.WithTimeout</code> para adicionar um deadline: se o servidor não responder em 1 segundo, a requisição é cancelada. Isso é essencial em sistemas distribuídos para evitar travamentos.</p>

<h2>Streaming em gRPC</h2>

<h3>Tipos de Streaming</h3>

<p>gRPC suporta quatro padrões de comunicação: unário (tradicional request-response), server streaming (servidor envia múltiplas mensagens), client streaming (cliente envia múltiplas mensagens) e bidirecional (ambos enviam múltiplas mensagens). Streaming é particularmente útil para dados em tempo real, download/upload de arquivos grandes ou processamento de fluxos de eventos.</p>

<h3>Definindo Streaming no Proto</h3>

<p>Vamos estender nosso arquivo <code>user.proto</code> com streaming:</p>

<pre><code class="language-protobuf">syntax = &quot;proto3&quot;;

package user;

option go_package = &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;;

message User {

int32 id = 1;

string name = 2;

string email = 3;

int32 age = 4;

}

message CreateUserRequest {

string name = 1;

string email = 2;

int32 age = 3;

}

message CreateUserResponse {

User user = 1;

string message = 2;

}

message ListUsersRequest {

int32 limit = 1;

}

message UserEvent {

User user = 1;

string event_type = 2;

}

service UserService {

rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);

rpc GetUser(User) returns (User);

rpc ListUsers(ListUsersRequest) returns (stream User);

rpc WatchUsers(stream User) returns (stream UserEvent);

}</code></pre>

<p>A palavra-chave <code>stream</code> define que aquele parâmetro pode ter múltiplas mensagens. <code>ListUsers</code> é server streaming (cliente envia uma requisição, servidor envia múltiplos usuários). <code>WatchUsers</code> é bidirecional (ambos enviam múltiplas mensagens).</p>

<h3>Implementando Server Streaming</h3>

<p>Atualize seu <code>server.go</code>:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;net&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

type userServer struct {

pb.UnimplementedUserServiceServer

users map[int32]*pb.User

nextID int32

}

func (s userServer) CreateUser(ctx context.Context, req pb.CreateUserRequest) (*pb.CreateUserResponse, error) {

s.nextID++

user := &amp;pb.User{

Id: s.nextID,

Name: req.Name,

Email: req.Email,

Age: req.Age,

}

s.users[user.Id] = user

return &amp;pb.CreateUserResponse{

User: user,

Message: &quot;User created successfully&quot;,

}, nil

}

func (s userServer) GetUser(ctx context.Context, req pb.User) (*pb.User, error) {

user, ok := s.users[req.Id]

if !ok {

return nil, fmt.Errorf(&quot;user not found&quot;)

}

return user, nil

}

func (s userServer) ListUsers(req pb.ListUsersRequest, stream grpc.ServerStream) error {

count := int32(0)

for _, user := range s.users {

if count &gt;= req.Limit &amp;&amp; req.Limit &gt; 0 {

break

}

if err := stream.SendMsg(user); err != nil {

return err

}

count++

}

return nil

}

func (s *userServer) WatchUsers(stream grpc.ServerStream) error {

for {

user := &amp;pb.User{}

if err := stream.RecvMsg(user); err != nil {

return err

}

event := &amp;pb.UserEvent{

User: user,

EventType: &quot;user_received&quot;,

}

if err := stream.SendMsg(event); err != nil {

return err

}

}

}

func main() {

listener, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)

if err != nil {

log.Fatalf(&quot;Failed to listen: %v&quot;, err)

}

grpcServer := grpc.NewServer()

pb.RegisterUserServiceServer(grpcServer, &amp;userServer{

users: make(map[int32]*pb.User),

nextID: 0,

})

fmt.Println(&quot;Server running on :50051&quot;)

if err := grpcServer.Serve(listener); err != nil {

log.Fatalf(&quot;Failed to serve: %v&quot;, err)

}

}</code></pre>

<p>Em <code>ListUsers</code>, usamos <code>stream.SendMsg()</code> para enviar cada usuário. O cliente receberá todos os usuários em sequência. Em <code>WatchUsers</code> (bidirecional), usamos <code>stream.RecvMsg()</code> para receber mensagens do cliente e <code>stream.SendMsg()</code> para responder.</p>

<h3>Cliente com Streaming</h3>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

func main() {

conn, err := grpc.Dial(&quot;localhost:50051&quot;, grpc.WithInsecure())

if err != nil {

log.Fatalf(&quot;Failed to connect: %v&quot;, err)

}

defer conn.Close()

client := pb.NewUserServiceClient(conn)

// Server streaming: ListUsers

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

defer cancel()

stream, err := client.ListUsers(ctx, &amp;pb.ListUsersRequest{Limit: 10})

if err != nil {

log.Fatalf(&quot;ListUsers failed: %v&quot;, err)

}

for {

user, err := stream.Recv()

if err != nil {

fmt.Println(&quot;Stream finished&quot;)

break

}

fmt.Printf(&quot;Received user: %v\n&quot;, user)

}

// Bidirecional streaming: WatchUsers

ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)

defer cancel()

stream2, err := client.WatchUsers(ctx)

if err != nil {

log.Fatalf(&quot;WatchUsers failed: %v&quot;, err)

}

// Enviar alguns usuários

go func() {

for i := 1; i &lt;= 3; i++ {

stream2.Send(&amp;pb.User{

Id: int32(i),

Name: fmt.Sprintf(&quot;User %d&quot;, i),

Email: fmt.Sprintf(&quot;user%d@example.com&quot;, i),

Age: 20 + int32(i),

})

time.Sleep(500 * time.Millisecond)

}

stream2.CloseSend()

}()

// Receber eventos

for {

event, err := stream2.Recv()

if err != nil {

fmt.Println(&quot;Watch finished&quot;)

break

}

fmt.Printf(&quot;Event: %v\n&quot;, event)

}

}</code></pre>

<p>Em streaming de cliente, chamamos <code>stream.Recv()</code> em um loop para receber mensagens até que o stream termine (erro EOF). Para bidirecional, executamos o envio em uma goroutine paralela enquanto a goroutine principal recebe eventos.</p>

<h2>Interceptors: Middleware Poderoso do gRPC</h2>

<h3>Entendendo Interceptors</h3>

<p>Interceptors são como middleware HTTP — eles interceptam chamadas de RPC antes de chegar ao handler e depois que a resposta é gerada. Use interceptors para logging, autenticação, autorização, rate limiting, métricas e observabilidade. Existem unary interceptors (para chamadas normais) e stream interceptors (para streaming).</p>

<h3>Implementando um Unary Interceptor</h3>

<p>Um exemplo prático: um interceptor que loga todas as chamadas RPC:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;net&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

type userServer struct {

pb.UnimplementedUserServiceServer

users map[int32]*pb.User

nextID int32

}

func (s userServer) CreateUser(ctx context.Context, req pb.CreateUserRequest) (*pb.CreateUserResponse, error) {

s.nextID++

user := &amp;pb.User{

Id: s.nextID,

Name: req.Name,

Email: req.Email,

Age: req.Age,

}

s.users[user.Id] = user

return &amp;pb.CreateUserResponse{

User: user,

Message: &quot;User created successfully&quot;,

}, nil

}

func (s userServer) GetUser(ctx context.Context, req pb.User) (*pb.User, error) {

user, ok := s.users[req.Id]

if !ok {

return nil, fmt.Errorf(&quot;user not found&quot;)

}

return user, nil

}

// Unary Interceptor para logging

func loggingUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

start := time.Now()

fmt.Printf(&quot;[%s] RPC iniciado: %s\n&quot;, time.Now().Format(time.RFC3339), info.FullMethod)

resp, err := handler(ctx, req)

duration := time.Since(start)

fmt.Printf(&quot;[%s] RPC finalizado: %s (duração: %v, erro: %v)\n&quot;,

time.Now().Format(time.RFC3339), info.FullMethod, duration, err)

return resp, err

}

func main() {

listener, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)

if err != nil {

log.Fatalf(&quot;Failed to listen: %v&quot;, err)

}

// Criar servidor com interceptor

grpcServer := grpc.NewServer(

grpc.UnaryInterceptor(loggingUnaryInterceptor),

)

pb.RegisterUserServiceServer(grpcServer, &amp;userServer{

users: make(map[int32]*pb.User),

nextID: 0,

})

fmt.Println(&quot;Server running on :50051&quot;)

if err := grpcServer.Serve(listener); err != nil {

log.Fatalf(&quot;Failed to serve: %v&quot;, err)

}

}</code></pre>

<p>O interceptor recebe o contexto, a requisição, informações do RPC e o handler (a função real que será executada). Ele pode inspecionar/modificar o contexto e requisição antes de chamar <code>handler()</code>, e processar a resposta/erro depois. Neste exemplo, medimos o tempo de execução.</p>

<h3>Interceptor de Autenticação</h3>

<p>Um exemplo mais prático: validar um token JWT:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;net&quot;

&quot;strings&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

&quot;google.golang.org/grpc/metadata&quot;

)

type userServer struct {

pb.UnimplementedUserServiceServer

users map[int32]*pb.User

nextID int32

}

func (s userServer) CreateUser(ctx context.Context, req pb.CreateUserRequest) (*pb.CreateUserResponse, error) {

s.nextID++

user := &amp;pb.User{

Id: s.nextID,

Name: req.Name,

Email: req.Email,

Age: req.Age,

}

s.users[user.Id] = user

return &amp;pb.CreateUserResponse{

User: user,

Message: &quot;User created successfully&quot;,

}, nil

}

func (s userServer) GetUser(ctx context.Context, req pb.User) (*pb.User, error) {

user, ok := s.users[req.Id]

if !ok {

return nil, fmt.Errorf(&quot;user not found&quot;)

}

return user, nil

}

// Interceptor de autenticação

func authUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

md, ok := metadata.FromIncomingContext(ctx)

if !ok {

return nil, fmt.Errorf(&quot;missing metadata&quot;)

}

tokens := md.Get(&quot;authorization&quot;)

if len(tokens) == 0 {

return nil, fmt.Errorf(&quot;missing authorization token&quot;)

}

token := tokens[0]

if !strings.HasPrefix(token, &quot;Bearer &quot;) {

return nil, fmt.Errorf(&quot;invalid token format&quot;)

}

token = strings.TrimPrefix(token, &quot;Bearer &quot;)

if token != &quot;valid-secret-token&quot; {

return nil, fmt.Errorf(&quot;invalid or expired token&quot;)

}

return handler(ctx, req)

}

func main() {

listener, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)

if err != nil {

log.Fatalf(&quot;Failed to listen: %v&quot;, err)

}

grpcServer := grpc.NewServer(

grpc.UnaryInterceptor(authUnaryInterceptor),

)

pb.RegisterUserServiceServer(grpcServer, &amp;userServer{

users: make(map[int32]*pb.User),

nextID: 0,

})

fmt.Println(&quot;Server running on :50051&quot;)

if err := grpcServer.Serve(listener); err != nil {

log.Fatalf(&quot;Failed to serve: %v&quot;, err)

}

}</code></pre>

<p>O servidor agora rejeita requisições sem um header <code>authorization</code> válido. O cliente precisa enviar esse header:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

&quot;time&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

&quot;google.golang.org/grpc/metadata&quot;

)

func main() {

conn, err := grpc.Dial(&quot;localhost:50051&quot;, grpc.WithInsecure())

if err != nil {

log.Fatalf(&quot;Failed to connect: %v&quot;, err)

}

defer conn.Close()

client := pb.NewUserServiceClient(conn)

// Adicionar metadata (header) com token

ctx := context.Background()

ctx = metadata.AppendToOutgoingContext(ctx, &quot;authorization&quot;, &quot;Bearer valid-secret-token&quot;)

ctx, cancel := context.WithTimeout(ctx, time.Second)

defer cancel()

resp, err := client.CreateUser(ctx, &amp;pb.CreateUserRequest{

Name: &quot;João Silva&quot;,

Email: &quot;joao@example.com&quot;,

Age: 28,

})

if err != nil {

log.Fatalf(&quot;CreateUser failed: %v&quot;, err)

}

fmt.Printf(&quot;Created user: %v\n&quot;, resp.User)

}</code></pre>

<h3>Stream Interceptor</h3>

<p>Para streaming, use <code>grpc.StreamInterceptor</code>:</p>

<pre><code class="language-go">package main

import (

&quot;context&quot;

&quot;fmt&quot;

&quot;log&quot;

pb &quot;github.com/seu-usuario/seu-projeto/pb/user&quot;

&quot;google.golang.org/grpc&quot;

)

type userServer struct {

pb.UnimplementedUserServiceServer

users map[int32]*pb.User

nextID int32

}

func (s userServer) ListUsers(req pb.ListUsersRequest, stream grpc.ServerStream) error {

count := int32(0)

for _, user := range s.users {

if count &gt;= req.Limit &amp;&amp; req.Limit &gt; 0 {

break

}

if err := stream.SendMsg(user); err != nil {

return err

}

count++

}

return nil

}

// Stream Interceptor para logging

func loggingStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {

fmt.Printf(&quot;Stream iniciado: %s\n&quot;, info.FullMethod)

err := handler(srv, ss)

fmt.Printf(&quot;Stream finalizado: %s (erro: %v)\n&quot;, info.FullMethod, err)

return err

}

func main() {

listener, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)

if err != nil {

log.Fatalf(&quot;Failed to listen: %v&quot;, err)

}

grpcServer := grpc.NewServer(

grpc.StreamInterceptor(loggingStreamInterceptor),

)

pb.RegisterUserServiceServer(grpcServer, &amp;userServer{

users: make(map[int32]*pb.User),

nextID: 0,

})

fmt.Println(&quot;Server running on :50051&quot;)

if err := grpcServer.Serve(listener); err != nil {

log.Fatalf(&quot;Failed to serve: %v&quot;, err)

}

}</code></pre>

<h3>Encadeando Múltiplos Interceptors</h3>

<p>Se precisar de vários interceptors, use bibliotecas como <code>go-grpc-middleware</code> ou encadeie manualmente:</p>

<pre><code class="language-go">func chainUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {

return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

for i := len(interceptors) - 1; i &gt;= 0; i-- {

next := handler

current := interceptors[i]

func(c grpc.UnaryServerInterceptor, n grpc.UnaryHandler) {

handler = func(ctx context.Context, req interface{}) (interface{}, error) {

return c(ctx, req, info, n)

}

}(current, next)

}

return handler(ctx, req)

}

}

// Uso

grpcServer := grpc.NewServer(

grpc.UnaryInterceptor(chainUnaryInterceptors(loggingUnaryInterceptor, authUnaryInterceptor)),

)</code></pre>

<p>Na prática, use <code>github.com/grpc-ecosystem/go-grpc-middleware</code> que oferece várias implementações prontas e testadas.</p>

<h2>Conclusão</h2>

<p>Você aprendeu que <strong>gRPC com Protocol Buffers oferece uma base sólida para sistemas distribuídos modernos</strong>, fornecendo serialização eficiente, tipagem forte e contrato claro entre cliente e servidor. A configuração inicial pode parecer verbosa (definir <code>.proto</code>, gerar código, implementar servidores), mas essa estrutura traz muito mais manutenibilidade do que APIs REST desorganizadas.</p>

<p>O <strong>streaming em gRPC resolve casos de uso que seriam ineficientes em REST</strong>, como monitoramento em tempo real, upload/download de arquivos grandes e comunicação bidirecional. Server streaming e bidirecional são primitivas poderosas que emergem naturalmente da arquitetura HTTP/2 do gRPC.</p>

<p>Por fim, <strong>interceptors são a chave para adicionar funcionalidades transversais sem poluir o código de negócio</strong>, simplificando autenticação, logging, rate limiting e observabilidade. Combine Protocol Buffers bem definidos, streaming apropriado e interceptors bem estruturados, e você terá uma base sólida para microsserviços de produção em Go.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://grpc.io/docs/languages/go/" target="_blank" rel="noopener noreferrer">gRPC Official Documentation - Go</a></li>

<li><a href="https://developers.google.com/protocol-buffers/docs/proto3" target="_blank" rel="noopener noreferrer">Protocol Buffers v3 Language Guide</a></li>

<li><a href="https://github.com/grpc/grpc-go" target="_blank" rel="noopener noreferrer">grpc-go GitHub Repository</a></li>

<li><a href="https://github.com/grpc-ecosystem/go-grpc-middleware" target="_blank" rel="noopener noreferrer">gRPC Ecosystem - Go Middleware</a></li>

<li><a href="https://www.youtube.com/watch?v=FMq5euaAkVw" target="_blank" rel="noopener noreferrer">Building Microservices with gRPC - Alex Xu</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Go

Guia Completo de Chi Router em Go: Roteamento Idiomático e Middlewares Componíveis
Guia Completo de Chi Router em Go: Roteamento Idiomático e Middlewares Componíveis

O que é Chi e Por que Ele Importa Chi é um roteador HTTP leve e expressivo pa...

O que Todo Dev Deve Saber sobre Goroutines em Go: Criação, Escalonamento e o Runtime do Go
O que Todo Dev Deve Saber sobre Goroutines em Go: Criação, Escalonamento e o Runtime do Go

O que são Goroutines Goroutines são funções que executam de forma concorrente...

O que Todo Dev Deve Saber sobre Garbage Collector em Go: Funcionamento Interno e Impacto na Performance
O que Todo Dev Deve Saber sobre Garbage Collector em Go: Funcionamento Interno e Impacto na Performance

Introdução: O que é o Garbage Collector em Go? O Garbage Collector (GC) é um...