gRPC: a candidate for your future projects?

French version on : www.programisto.fr

What is it?

gRPC, which stands for Remote Procedure Call, is a cross-platform, open-source communication protocol based on contracts. It simplifies and manages interservice communication by exposing a set of functions to external clients.

Protobuf and HTTP/2 for performance

By default, gRPC uses Protocol Buffers (Protobuf), a serialization system similar to JSON or XML. The system relies on .proto files that serve as interfaces and describe our object. The source code for the object can then be generated in several languages.

gRPC clients and servers can run and communicate with each other from different environments. For example, a gRPC server in C# can support clients in JavaScript, Java, C#, and any other language.

Furthermore, designed for HTTP/2, gRPC allows for more efficient communication by leveraging bidirectional streaming and Transport Layer Security (TLS) features.

It is important to note that gRPC is not the only technology that can use HTTP/2 and benefit from its performance improvements. REST APIs can also use HTTP/2.

Putting it into practice: implementation example

The easiest way to explore how gRPC works is to start a project to illustrate its implementation.

Creating the API project

Visual Studio 2022 provides us with a template for creating a gRPC service. We will select ASP.NET Core gRPC Service and then choose the .NET 6.0 LTS framework.

To provide a more meaningful example, we will create a library management API, which we will call Library.

In our solution, you will now find two files generated by Visual Studio:

  • greet.proto: the contract of our API
  • GreeterService.cs: the service class of our API.

Creating the contract

We will begin by defining our contract in order to retrieve information about a book in our library. To do this, we will first rename the greet.proto file to product.proto.

Next, we will define the function to return a product by inputting its ID, following the documentation for proto3:

syntax = "proto3";

option csharp_namespace = "LibraryAPI";

package book;

service BookCatalog {
    rpc GetBookDetails (BookDetailsRequest) returns (BookDetailsReply);
}

message BookDetailsRequest {
    int32 id = 1;
}

message BookDetailsReply {
    int32 id = 1;
    string name = 2;
    repeated Author author = 4;
}

message Author {
    string firstname = 1;
    string latname = 2;
}

Creating the service

Similarly, we will rename GreeterService.cs to BookService.cs.

Then, we will hard-code our service to return the details of a book following the specifications of the contract in the book.proto file, for example, as shown below:

public override Task GetBookDetails(
                   BookDetailsRequest request, ServerCallContext context)
        {
            var booksDetail = new BookDetailsReply
            {
                Id = request.Id,
                Name = "Element of Reusable Object-Oriented Software"

            };

            var authors = new List()
            {
                new Author() { Firstname = "Erich",  Lastname = "Gamma"},
                new Author() { Firstname = "Richard",  Lastname = "Helm"},
                new Author() { Firstname = "Ralph",  Lastname = "Johnson"},
                new Author() { Firstname = "John",  Lastname = "Vlissides"},
            };

            booksDetail.Authors.AddRange(authors);

            return Task.FromResult(booksDetail);
        }
    }

To make the service work, it is necessary to modify Program.cs to reference the new name of the service. In our case, we will rename app.MapGrpcService<GreeterService>(); to app.MapGrpcService<BookService>(); to make our new API executable.

Now, we can launch our project and we have a running API. However, if we try to access it from a browser, we get the following message:

Indeed, we need a gRPC client to establish communication with this new endpoint.

Creating the client

For creating the client, you can use your preferred language as announced in the presentation. Here, we will use a basic Console App in C# to call our API, which we will name LibraryClientApp.

Adding NuGet packages

In order for our client to work, it needs the following NuGet packages:

  • Grpc.Net.Client, containing the .NET Core client,
  • Google.Protobuf, the protobuf library for C#,
  • Grpc.Tools, the C# toolbox for protobuf files.

Adding the contract

As a reminder, the functioning of gRPC is based on the sharing of the contract. We will therefore create a folder named Protos in which we will copy the book.proto from our service.

Then, we need to update the namespace of the .proto with the namespace of our project: csharp option csharp_namespace = "LibraryClientApp";

To make sure that the .proto is correctly recognized by our application, we must add an item group in the .csproj as follows: 7

<ItemGroup> 
    <Protobuf Include="Protosbook.proto" GrpcServices="Client" />
</ItemGroup>

Consumption of the API

Now that our contract is properly registered in our client, we need to modify our program.cs to perform the necessary actions to consume our API:

  • Create the channel that represents the location of the service endpoint. As the port may vary, we advise you to consult the launchsettings.json file to get the actual value, Create the client object,
  • Build a simple request,
  • Send the request
using System.Text.Json;
using Grpc.Net.Client;
using LibraryApp;

var channel = GrpcChannel.ForAddress("https://localhost:7200");
var client = new BookCatalog.BookCatalogClient(channel);

var request = new BookDetailsRequest
{
    Id = 1
};

var response = await client.GetBookDetailsAsync(request);

Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions
{
    WriteIndented = true
}));

Console.ReadKey();

We can now run both of our projects. As a result of our client, we successfully obtain the information of our book.

{
  "Id": 1,
  "Name": "Element of Reusable Object-Oriented Software",
  "Authors": [
    {
      "Firstname": "Erich",
      "Lastname": "Gamma"
    },
    {
      "Firstname": "Richard",
      "Lastname": "Helm"
    },
    {
      "Firstname": "Ralph",
      "Lastname": "Johnson"
    },
    {
      "Firstname": "John",
      "Lastname": "Vlissides"
    }
  ]
}

Conclusion

As we have seen through our example, gRPC offers relatively easy handling. It can be perfectly suitable for a scenario such as a microservice where efficiency is essential, as it is designed for low latency and high-throughput communication.

The dilemma may arise with REST, and some situations will lead you to choose one over the other. Nevertheless, we encourage you to try gRPC, as it will give you a head start on the future of APIs.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *