Introduction to gRPC on .NET

Introduction to gRPC on .NET

Why gRPC ?

gRPC is a modern high performance universal Remote Procedure Call Framework.

It uses HTTP/2 protocol for communication and we will see some of the benefits of using HTTP/2 over the HTTP/1.1 protocol.
HTTP/1.1 protocol has been there since a long time when it was released in 1997.

Let us first try to understand the problems with the HTTP/1.1 protocol so that we can then understand how gRPC solves the problem with the power of HTTP/2.

Brief history of HTTP

HTTP is an application layer protocol that exists on top of TCP/IP which is a transport layer protocol.

TCP/IP is a connection oriented protocol which means requesting for a resource with the HTTP would require to establish a TCP connection first and then the request can be sent over the HTTP to get a response from the server.

Establishing a TCP connection is costly and it requires resources.

Problem with HTTP1.x protocol

With HTTP/1 protocol every request had to establish a TCP connection.
The connection was terminated after getting the response from the server.
However the single TCP connection can be used for issuing multiple requests by using the connection keep-alive headers being used with HTTP/1.

With HTTP/1.1 protocol it was possible to issue multiple requests within the same TCP connection without the need of specifying the keep-alive header since it supported persistent connection by default.

This led to a more efficient communication since the single TCP connection can be used for making multiple request thus saving us from spending resources to establish TCP connections multiple times.

The feature of issuing multiple requests with the same connection is termed as request pipelining.

In HTTP pipelining multiple requests are issued without waiting for the response from the previously sent requests.

Although HTTP/1.1 allows persistent TCP connections and HTTP Pipelining there is a limitation that the server should send back the response in the sequence in which the requests where received.
This makes the entire connection FIFO and leading to a problem called Head of Line Blocking (HOL).
In this the newer request which could have been processed is waiting for the response of the request ahead of it in the queue.

Also there is a limit to the number of TCP connections in browsers between two hosts to serve the requests. Most browsers allow 6 TCP connections simultaneously.

These limitations of HTTP/1.1 caused network latency which was not much evident earlier. The internet communication at that time was fairly simple.
The communication these days require a lot of content to be transferred across the wire where even the slightest latency would make the network inefficient and slow with the demand of high performance and efficiency.

Features of HTTP2

HTTP/2 comes up with some of the features that solves some of these issues.

gRPC uses HTTP/2 as the application layer transfer protocol however the transport layer protocol is still TCP.

Some of the features of the HTTP/2 protocol are as follows -

  1. Binary Framing Protocol for Data Transport
  2. Multiplexing
  3. Bidirectional full-duplex communication
  4. Streaming
  5. Header Compression

Do you know how to check if the application uses HTTP2 protocol ?

You can refer to my post Http2 Protocol Check in Chrome to see the HTTP protocol used by an application from the web developer console.

gRPC uses HTTP/2 for its transport protocol as compared to REST that uses HTTP/1.1 hence resolving all the issues of the prior versions of HTTP as discussed above and comes up with better performance and efficiency.

Usecase of gRPC

If you are working on legacy project that uses .NET Remoting as the RPC framework then migrating your application to .NET Core using gRPC would a good idea since .NET Core does not support remoting anymore.

To support the migration of applications using WCF, Microsoft has recently released the CoreWCF 1.0 but the recommendation is to use it only if the current application is strongly dependent on the WCF.

CoreWCF would still be communication over HTTP/1.1 thus not having the fetaures of HTTP/2.

If you are starting a new project then using gRPC as a service would be a good idea to use the features coming up with HTTP/2.

gRPC in .NET

Now that we have a background of what is the problem getting solved with gRPC that uses HTTP/2 protocol we will now look into the implementation of gRPC service in .NET.

gRPC is integrated into .NET Core 3.0 SDK or later and supported by dotnet CLI. Let us now create a sample grpc service and consume it from two different clients -

  1. Web API
  2. Console App

Note : We would be using the .NET 6 for creating both the server and the client projects with VSCode.

  1. Create a folder and open it in VSCode.
  2. Create a new solution with the following command.
   dotnet new sln -o grpcDemo
   cd grpcDemo
  1. Create a new grpcService project and add it to the solution.
   dotnet new grpc -o GrpcService
   dotnet sln add GrpcService  

This command creates a new grpc service project using the CLI default template.
As we can see in the figure below that there are 2 special folders that gets created.

![Grpc Service](/images/posts/grpc-service.JPG)
  1. Protos - This folder contains a file named greet.proto. The greet.proto is a protobuffer file that is authored using the Interface Definition Language(IDL). This is written to specify the methods in the service along with their input and outputs and also contains the structure of the inputs and outputs. This file is used to generate the language specific client and server code which could be utilized for the communication across computers. If you have worked on WCF you might be aware of the WSDL file that is used to generate the proxy for the service at the client side to consume the service. The .proto file does the same thing in grpc.

greet.proto

 syntax = "proto3";

 option csharp_namespace = "GrpcService";

 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;
 }

The code above shows that there is a service named greeter that contains a method named sayHello which takes HelloRequest as the input parameter and returns HelloReply as the output.

  1. Services/GreeterService.cs
using Grpc.Core;
using GrpcService;

namespace GrpcService.Services;

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
        });
    }
}

The GreeterService.cs is a class that contains the server side implementation of the greeter service defining the SayHello method that could be consumed by the client generated from the proto file. 3. Run the GrpcService

 dotnet build 
 dotnet run 

Notice the port on which the service is running because we would be using the same URL for connecting to the service from the client.

  1. Create a webapi Client Project and add it to the solution.
    dotnet new webapi -o grpcApiClient
    dotnet sln add grpcApiClient  
    cd grpcApiClient

Copy the protos folder from the service and paste it inside the grpcApiClient project. We are doing this so that the appropriate proxies can be generated which can be used by the client code to invoke the service. Change the namespace in the greet.proto file to "grpcApiClient" so that the proxies would be generated with the same namespace. Edit the grpcApiClient.csproj and include the following -

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

Add the required nuget packages in the client project. By adding these packages the client project would automatically generate the proxies when the project is build.

    dotnet add grpcApiClient.csproj package Grpc.Net.Client
    dotnet add grpcApiClient.csproj package Google.Protobuf
    dotnet add grpcApiClient.csproj package Grpc.Tools

Build the project using -

    dotnet build

Notice that the Greet.cs and GreetGrpc.cs files gets autogenerated within the obj folder inside protos once the build is succeeded. Grpc Api Client

Let us now invoke the grpcService from the WeatherController in the grpcApiClient project. Please make the changes to the WeatherForecastController as per the below code.

    using Microsoft.AspNetCore.Mvc;
    using System.Threading.Tasks;
    using Grpc.Net.Client;
    namespace grpcApiClient.Controllers;

    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }

        [HttpGet]
        [Route("Greet")]
        public async Task<string> Greet()
        {
            // The port number must match the port of the gRPC server.
            using var channel = GrpcChannel.ForAddress("https://localhost:7241");
            var client = new Greeter.GreeterClient(channel);
            var reply = await client.SayHelloAsync(
                            new HelloRequest { Name = "GreeterClient" });
            return reply.Message;
        }
    }

Now run the API and invoke the below URL from the browser to see the result.

    https://localhost:7250/WeatherForecast/greet

Grpc Api Result

  1. Create a console client project.
   dotnet new console -o grpcConsoleClient

Copy the protos folder just like the way we did for grpcApiClient project and modify the grpcConsoleClient.csproj file to include the below code.

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

Add the required nuget packages in the client project. By adding these packages the client project would automatically generate the proxies when the project is build.

    dotnet add grpcConsoleClient.csproj package Grpc.Net.Client
    dotnet add grpcConsoleClient.csproj package Google.Protobuf
    dotnet add grpcConsoleClient.csproj package Grpc.Tools

Build the project using dotnet build

Let us now invoke the grpcService from the grpcConsoleClient project. Please make the changes to the Program.cs as per the 
below code.
        using System.Threading.Tasks;
        using Grpc.Net.Client;
        using grpcConsoleClient;

        // The port number must match the port of the gRPC server.
                using var channel = GrpcChannel.ForAddress("https://localhost:7241");
                var client = new Greeter.GreeterClient(channel);
                var reply = await client.SayHelloAsync(
                                new HelloRequest { Name = "GreeterClient" });
        Console.WriteLine("Greeting: " + reply.Message);
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();

Run the project and see it in action.

Conclusion

In this post we have seen

  1. What is gRPC and Why we should use gRPC ?. gRPC is useful if we want to create new services which is high performing. The communication with gRPC is fast and reliable because of the HTTP2 features like Header Compression, Multiplexing , Binary Framing, Streaming large datasets.
    gRPC is ideal if there are multiple languages and technologies used in the project since the .proto file is supported in many languages for implementing the client and server code.
  2. The history of the HTTP and the problems with the HTTP1.1 which has been there since many years.
  3. The benefits of HTTP2 vs HTTP1.1
  4. Features introduced with HTTP2.
  5. How to create a sample gRPC service in .NET.
  6. Consuming gRPC service with a .NET6 Console App and a Web API.

Please share the post by clicking the social media icons at the beginning of the post.

Thank you for reading and see you in the next post !👋

Buy a coffee for sudshekhar