gRPC é o protocolo de comunicação inter-serviço mais eficiente para microsserviços .NET. Enquanto REST usa JSON sobre HTTP/1.1, gRPC usa Protobuf binário sobre HTTP/2 — gerando payloads até 10× menores e latência mais baixa. O contrato é definido em .proto, e o código C# é gerado automaticamente: zero chance de inconsistência entre cliente e servidor.
Setup: servidor gRPC em ASP.NET Core
dotnet add package Grpc.AspNetCore// Program.cs: builder.Services.AddGrpc(options => { options.MaxReceiveMessageSize = 4 * 1024 * 1024; // 4MB options.MaxSendMessageSize = 4 * 1024 * 1024; options.EnableDetailedErrors = builder.Environment.IsDevelopment(); });
builder.Services.AddGrpcReflection(); // permite inspeção via Postman/grpcurl em dev
app.MapGrpcService<ProdutosGrpcService>(); app.MapGrpcService<PedidosGrpcService>();
if (app.Environment.IsDevelopment()) app.MapGrpcReflectionService(); // /grpc.reflection.v1alpha.ServerReflection
Definindo o contrato com Protobuf
// Protos/produtos.proto:
syntax = "proto3";
option csharp_namespace = "NeryxApp.Grpc";
package produtos;
service ProdutosService {
// Unário: request → response (como REST)
rpc GetProduto (GetProdutoRequest) returns (ProdutoResponse);
rpc CriarProduto (CriarProdutoRequest) returns (ProdutoResponse);
// Server streaming: request → múltiplos responses
rpc ListarProdutos (ListarProdutosRequest) returns (stream ProdutoResponse);
// Client streaming: múltiplos requests → response
rpc ImportarProdutos (stream CriarProdutoRequest) returns (ImportacaoResponse);
// Bidirecional: múltiplos requests ↔ múltiplos responses
rpc SincronizarEstoque (stream EstoqueUpdate) returns (stream EstoqueConfirmacao);
}
message GetProdutoRequest {
string id = 1; // campo 1: id (number = tag, não o valor)
}
message CriarProdutoRequest {
string nome = 1;
double preco = 2;
string categoria = 3;
int32 estoque = 4;
}
message ProdutoResponse {
string id = 1;
string nome = 2;
double preco = 3;
string categoria = 4;
int32 estoque = 5;
string criado_em = 6;
}
message ListarProdutosRequest {
string categoria = 1; // filtro opcional
int32 page_size = 2; // default 0 = sem limite
}
message ImportacaoResponse {
int32 total_importados = 1;
int32 total_erros = 2;
repeated string erros = 3;
}
message EstoqueUpdate {
string produto_id = 1;
int32 quantidade = 2;
string operacao = 3; // "incrementar" | "decrementar" | "definir"
}
message EstoqueConfirmacao {
string produto_id = 1;
int32 estoque_atual = 2;
bool sucesso = 3;
string mensagem = 4;
}
// .csproj — configurar geração automática de código C#:
<ItemGroup>
<Protobuf Include="Protos/produtos.proto" GrpcServices="Server" />
<!-- GrpcServices="Client" para gerar apenas cliente -->
<!-- GrpcServices="Both" para gerar ambos -->
</ItemGroup>
Implementando o serviço (servidor)
// A classe base ProdutosServiceBase é gerada automaticamente pelo Protobuf: public class ProdutosGrpcService : ProdutosService.ProdutosServiceBase { private readonly IProdutoRepository _repo;public ProdutosGrpcService(IProdutoRepository repo) => _repo = repo; // Unário: public override async Task<ProdutoResponse> GetProduto( GetProdutoRequest request, ServerCallContext context) { var produto = await _repo.GetByIdAsync(Guid.Parse(request.Id), context.CancellationToken); if (produto is null) throw new RpcException(new Status(StatusCode.NotFound, $"Produto {request.Id} não encontrado")); return MapToResponse(produto); } // Server streaming — envia produtos um por um: public override async Task ListarProdutos( ListarProdutosRequest request, IServerStreamWriter<ProdutoResponse> responseStream, ServerCallContext context) { // Busca em lotes para não carregar tudo em memória: await foreach (var produto in _repo.StreamByCategoriaAsync( request.Categoria, context.CancellationToken)) { if (context.CancellationToken.IsCancellationRequested) break; await responseStream.WriteAsync(MapToResponse(produto)); } } // Client streaming — recebe lote de criações: public override async Task<ImportacaoResponse> ImportarProdutos( IAsyncStreamReader<CriarProdutoRequest> requestStream, ServerCallContext context) { var importados = 0; var erros = new List<string>(); await foreach (var request in requestStream.ReadAllAsync(context.CancellationToken)) { try { await _repo.CriarAsync(MapToEntity(request), context.CancellationToken); importados++; } catch (Exception ex) { erros.Add($"Erro ao importar '{request.Nome}': {ex.Message}"); } } return new ImportacaoResponse { TotalImportados = importados, TotalErros = erros.Count, Erros = { erros } }; } // Streaming bidirecional — sincronização de estoque: public override async Task SincronizarEstoque( IAsyncStreamReader<EstoqueUpdate> requestStream, IServerStreamWriter<EstoqueConfirmacao> responseStream, ServerCallContext context) { await foreach (var update in requestStream.ReadAllAsync(context.CancellationToken)) { var estoque = await _repo.AtualizarEstoqueAsync( Guid.Parse(update.ProdutoId), update.Quantidade, update.Operacao); await responseStream.WriteAsync(new EstoqueConfirmacao { ProdutoId = update.ProdutoId, EstoqueAtual = estoque, Sucesso = true }); } } private static ProdutoResponse MapToResponse(Produto p) => new() { Id = p.Id.ToString(), Nome = p.Nome, Preco = (double)p.Preco, Categoria = p.Categoria, Estoque = p.Estoque, CriadoEm = p.CriadoEm.ToString("O") };
}
Cliente gRPC em outro microsserviço
dotnet add package Grpc.Net.Client dotnet add package Google.Protobuf dotnet add package Grpc.Tools// Registrar cliente com HttpClientFactory (recomendado em produção): builder.Services.AddGrpcClient<ProdutosService.ProdutosServiceClient>(options => { options.Address = new Uri(builder.Configuration[“Services:Produtos:GrpcUrl”]!); // Ex: “https://produtos-service:5001” }) .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan, KeepAlivePingDelay = TimeSpan.FromSeconds(60), KeepAlivePingTimeout = TimeSpan.FromSeconds(30), EnableMultipleHttp2Connections = true // connection pooling HTTP/2 }) .AddCallCredentials((context, metadata) => { // Propaga token JWT do request atual para o microsserviço downstream var httpContext = context.ServiceProvider .GetRequiredService<IHttpContextAccessor>().HttpContext; var token = httpContext?.Request.Headers[“Authorization”].ToString() .Replace(“Bearer ”, ""); if (!string.IsNullOrEmpty(token)) metadata.Add(“Authorization”, $“Bearer {token}”); return Task.CompletedTask; }, GrpcChannelOptions.Empty);
// Uso no serviço: public class PedidoService { private readonly ProdutosService.ProdutosServiceClient _produtosClient;
public PedidoService(ProdutosService.ProdutosServiceClient client) => _produtosClient = client; public async Task<PedidoDto> CriarPedidoAsync(CriarPedidoRequest request, CancellationToken ct) { // Valida produto via gRPC (comunicação interna eficiente) var produto = await _produtosClient.GetProdutoAsync( new GetProdutoRequest { Id = request.ProdutoId }, cancellationToken: ct); if (produto.Estoque < request.Quantidade) throw new InvalidOperationException("Estoque insuficiente"); // ... cria pedido }
}
Interceptors: cross-cutting concerns no gRPC
// Equivalente ao middleware ASP.NET Core, mas para gRPC: public class LoggingInterceptor : Interceptor { private readonly ILogger<LoggingInterceptor> _logger;public LoggingInterceptor(ILogger<LoggingInterceptor> logger) => _logger = logger; // Intercepta chamadas unárias: public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>( TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation) { var sw = Stopwatch.StartNew(); try { var response = await continuation(request, context); _logger.LogInformation("gRPC {Method} concluído em {Ms}ms", context.Method, sw.ElapsedMilliseconds); return response; } catch (Exception ex) { _logger.LogError(ex, "gRPC {Method} falhou após {Ms}ms", context.Method, sw.ElapsedMilliseconds); throw; } }}
// Registrar globalmente: builder.Services.AddGrpc(options => options.Interceptors.Add<LoggingInterceptor>());
gRPC vs REST vs Mensageria: quando usar cada um
gRPC é ideal para comunicação síncrona entre microsserviços internos onde performance importa — payloads menores, streaming bidirecional, contrato fortemente tipado. Use quando dois serviços precisam de resposta imediata e rodam na mesma rede privada.
REST/HTTP é a escolha para APIs públicas consumidas por apps web e mobile — amplamente suportado, fácil de debugar com ferramentas padrão, sem dependência de cliente gerado.
Mensageria (RabbitMQ, SQS) é para comunicação assíncrona onde o produtor não precisa esperar o consumidor — processamento em background, eventos de domínio, fanout para múltiplos consumidores.
Conclusão
gRPC no .NET elimina a inconsistência de contratos entre microsserviços — o código C# é gerado a partir do .proto, então quebras de contrato viram erros de compilação antes de chegar em produção. O streaming bidirecional resolve casos como sincronização de dados e dashboards internos de forma nativa. Para comunicação interna entre serviços .NET, gRPC é a escolha mais robusta em 2026.
Se você está desenhando a comunicação entre microsserviços e precisa decidir entre gRPC, REST e mensageria, a Neryx pode ajudar com a análise e implementação. Consultoria inicial gratuita.
Leitura complementar: