Serviços gRPC no ASP.NET Core

No âmbito de aplicações, quando estamos falando de serviços, o desenvolvedor imagina uma API RESTful. Não dá para negar que o casamento de HTTP+JSON faz um grande sucesso, mas em alguns momentos esta combinação possui limitações.

Em larga escala, ou quando se lida com aplicações críticas, a performance de uma API REST pode significar seu gargalo, principalmente quando estamos lidando com microserviços. É por causa disso que grandes empresas, como Netflix, Digital Ocean, SoundClound e Google, optaram pelo gRPC para melhorar e otimizar a comunicação dos seus microserviços.

O gRPC é um framework RPC open source criado pelo Google, como forma de melhorar a comunicação do grande número de microserviços que possuem. Com suporte à várias linguagens, na versão 3.0 do .NET Core 3.0, a Microsoft adicionou um template que facilita a criação de serviços gRPC.

Antes de conhecer este template e criar um serviço, você precisa conhecer mais sobre o gRPC.

C# (C Sharp) - APIs REST com ASP.NET Web API
Curso de C# (C Sharp) - APIs REST com ASP.NET Web API
CONHEÇA O CURSO

O que seria o gRPC?

Como dito, o gRPC é um framework RPC. O RPC é a sigla de Remote Procedure Call, chamada de procedimento remoto, que basicamente trata-se de uma tecnologia para comunicação de processos. Permitindo que um computador possa invocar um procedimento de outro computador, independente da linguagem ou plataforma.

No manifesto de motivação e princípios do gRPC o Google conta que já utilizava uma infraestrutura RPC denominada Stubby. Mas por ser muito associada a infraestrutura da empresa e não ser baseado em nenhum padrão, ela não poderia ser aberta a todos. Com o advento do HTTP/2, o Stubby foi refeito e nasceu o gRPC, que hoje é parte da CNCF, Cloud Native Computer Foundation.

Porque o HTTP/2 é importante?

O gRPC não trabalha sob o protocolo HTTP 1.1, que é o protocolo utilizado por todos os servidores web, devido suas limitações. A principal é a forma que as solicitações são tratadas.

Por mais que não seja aparente, no HTTP 1.1 um servidor só pode receber e responder uma requisição por vez. Para tentar contornar este limite, ele trabalha com paralelismo, mas o canal de comunicação trabalha em apenas uma via. Quando está recebendo não pode enviar e vice-versa.

Já no HTTP/2 as conexões são “multiplexadas” (multiplex), o que significa que trabalha de forma bidirecional, recebendo e enviando várias solicitações ao mesmo tempo.

As duas imagens abaixo ilustram este cenário:

Aqui é importante ressaltar que devido ao uso do HTTP/2, um serviço do gRPC só pode ser hospedado em um servidor que fornece suporte a esta versão do protocolo. Felizmente na versão 3.0 do .NET Core, o Kestrel passou a suportar esta versão.

Para conhecer as características deste tipo de serviço, vamos colocar a mão na massa.

Criando um serviço gRPC no ASP.NET Core

A partir da versão 3.0 do .NET core é possível criar um serviço gRPC através do comando abaixo:

dotnet new grpc -o ServicoGrpc

Ou no Visual Studio 2019, selecione o template gRPC Service.

A aplicação criada terá a estrutura abaixo:

Vamos ver em detalhes seus arquivos.

Contrato com Protobuf

Dentro da pasta Protos temos o arquivo greet.proto:

syntax = "proto3";

option csharp_namespace = "ServicoGrpc";

package Greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

Este é o arquivo de contrato. Baseado no padrão Protobuf, ele que definirá a estrutura do serviço gRPC e precisa existir no cliente e no servidor. É a partir dele que o gRPC irá gerar as operações do serviço.

Acima é definido dentro do pacote Greet um serviço Greeter, que contém uma operação, SayHello. Em seguida é definida a estrutura dos dados recebidos na solicitação e os dados retornados.

Nos bastidores, a biblioteca do gRPC, Grpc.AspNetCore, irá criar classes, que nos permitirá definir o serviço no C#.

Serviços

Os serviços são definidos dentro da pasta Services, que no momento possui apenas a classe GreeterService:

public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

Esta classe herda Greeter.GreeterBase, que foi gerada pelo gRPC com base no arquivo .proto. O mesmo ocorreu com as classes HelloRequest e HelloReply.

Servidor

Por fim, no arquivo appsettings.json é definido que o Kestrel utilizará o protocolo HTTP/2:

"Kestrel": {
  "EndpointDefaults": {
    "Protocols": "Http2"
  }
}

E na classe Startup, além de registrar o serviço gRPC, cria-se um fallback para caso de o usuário acessar o serviço pelo navegador ou utilizando um client sem suporte ao gRPC:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<GreeterService>();

            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
            });
        });
    }
}

Cliente

Para acessar um serviço gRPC necessitamos de um cliente com suporte a este tipo de serviço, para isso, irei utilizar uma aplicação console simples:

class Program
{
    static async Task Main(string[] args)
    {
        var channel = GrpcChannel.ForAddress("https://localhost:5001");
        var client =  new Greeter.GreeterClient(channel);
        var reply = await client.SayHelloAsync(
                        new HelloRequest { Name = "Treinaweb Blog" });
        Console.WriteLine("Saudacao: " + reply.Message);
        Console.WriteLine("Pressione qualquer coisa para sair...");
        Console.ReadKey();
    }
}

Que precisa referenciar as bibliotecas Google.Protobuf, Grpc.Net.Client e Grpc.Tools:

<ItemGroup>
  <PackageReference Include="Google.Protobuf" Version="3.11.1" />
  <PackageReference Include="Grpc.Net.Client" Version="2.25.0" />
  <PackageReference Include="Grpc.Tools" Version="2.25.0">
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>

Além claro do arquivo greet.proto definido do servidor:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

No servidor, o atributo GrpcServices é definido com o valor Server.

Ao executar ambas as aplicações, inicialmente o serviço e por fim o cliente, poderemos ver a comunicação sendo realizada:

GraphQL - Criando APIs modernas com Graphcool e Apollo
Curso de GraphQL - Criando APIs modernas com Graphcool e Apollo
CONHEÇA O CURSO

Conclusão

No momento há poucos servidores web com suporte ao gRPC, mas esta tecnologia possui muito potencial. Em uma arquitetura de microserviços é altamente recomendado que ela seja utilizada para a comunicação entre os microserviços. Neste ambiente a comunicação externa pode se manter com a tradicional combinação HTTP+JSON.

Até a próxima.

Deixe seu comentário

Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.