Book Image

Microservices Communication in .NET Using gRPC

By : Fiodar Sazanavets
Book Image

Microservices Communication in .NET Using gRPC

By: Fiodar Sazanavets

Overview of this book

Explore gRPC's capabilities for faster communication between your microservices using the HTTP/2 protocol in this practical guide that shows you how to implement gRPC on the .NET platform. gRPC is one of the most efficient protocols for communication between microservices that is also relatively easy to implement. However, its official documentation is often fragmented and.NET developers might find it difficult to recognize the best way to map between C# data types and fields in gRPC messages. This book will address these concerns and much more. Starting with the fundamentals of gRPC, you'll discover how to use it inside .NET apps. You’ll explore best practices for performance and focus on scaling a gRPC app. Once you're familiar with the inner workings of the different call types that gRPC supports, you'll advance to learning how to secure your gRPC endpoints by applying authentication and authorization. With detailed explanations, this gRPC .NET book will show you how the Protobuf protocol allows you to send messages efficiently by including only the necessary data. You'll never get confused again while translating between C# data types and the ones available in Protobuf. By the end of the book, you’ll have gained practical gRPC knowledge and be able to use it in .NET apps to enable direct communication between microservices.
Table of Contents (17 chapters)
1
Section 1: Basics of gRPC on .NET
5
Section 2: Best Practices of Using gRPC
9
Section 3: In-Depth Look at gRPC on .NET

Setting up a gRPC client

Now, we will add a basic gRPC client that will be able to communicate with our service via gRPC. This will be a basic console application. The process will consist of the following steps:

  1. Initialize the console application project.
  2. Add some gRPC client dependencies to the project.
  3. Add some code to connect to the gRPC client.

Once you've followed these steps, your basic console application will be able to send requests to the gRPC server and receive responses from it.

Initializing the project for the client application

If you are using an IDE, you can add a new project to your solution. The template that you will need is called Console Application or Console Project, depending on which IDE you're using. However, you need to make sure that you don't choose the .NET Framework version of the template, which will be clearly labeled. Likewise, make sure that you select the C# template as the IDE may present you with options for other languages too:

Figure 1.9 – Console Application template on Visual Studio 2019

Figure 1.9 – Console Application template on Visual Studio 2019

As console application is a very basic application type; there won't be any complex setup options to select while creating the project. You can leave all the default options selected. Let's call our new project BasicGrpcClient.

If you are using a code editor and CLI instead of a fully-fledged IDE, you can create the project by executing a dotnet CLI command. Please ensure that you execute this command from the folder where the BasicGrpcService project folder is located. It will create the new project folder at the same level inside of your filesystem that your original project folder is located at. The command will be as follows:

dotnet new console -o BasicGrpcClient

Adding gRPC client components to the application

Now, to make your console application act as a gRPC client, you will need to add some NuGet references to your project. You can do so by executing the following commands inside your BasicGrpcClient project folder:

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

Once the packages have been installed, the content of your BasicGrpcClient.csproj file should be similar to this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Google.Protobuf"
      Version="3.17.3" />
    <PackageReference Include="Grpc.Net.Client"
      Version="2.38.0" />
    <PackageReference Include="Grpc.Tools" Version="2.38.1">
      <IncludeAssets>runtime; build; native; contentfiles; 
        analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
</Project>

Next, we will need to add the following section to the project file, which references the proto file we will be using to communicate with the server:

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

This will make our project file look like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Google.Protobuf"
      Version="3.17.3" />
    <PackageReference Include="Grpc.Net.Client"
      Version="2.38.0" />
    <PackageReference Include="Grpc.Tools" Version="2.38.1">
      <IncludeAssets>runtime; build; native; contentfiles; 
        analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Protobuf Include="Protos\greeter.proto"
      GrpcServices="Client" />
  </ItemGroup>
</Project>

This is similar to what we've done with the server application. But this time, we are telling our application to only generate code for the client components. This is why we have set the GrpcServices attribute to Client.

Because we will be connecting to the server we created previously, we need a proto file with package, service, rpc, and message definitions that are identical to what we had in our server-side proto file. However, our C# namespace can be different.

Therefore, what you'll need to do next is create a Protos folder inside your BasicGrpsClient project folder. Then, you must insert the greeter.proto file into this folder and ensure it has the following content:

syntax = "proto3";
option csharp_namespace = "BasicGrpcClient";
 package greeter;
 
// The greetings manager service definition.
service GreetingsManager {
  // Request the service to generate a greeting message.
  rpc GenerateGreeting (GreetingRequest) returns 
    (GreetingResponse);
}
 
// The request message definition containing the name to be 
  addressed in the greeting message.
message GreetingRequest {
  string name = 1;
}
 
// The response message definition containing the greeting   text.
message GreetingResponse {
  string greetingMessage = 1;
}

Please note that this file is identical to the one we have in our server application project, except for the csharp_namespace element. This element is used by the gRPC tools inside your specific .NET project and it does not affect compatibility between the server and client versions of the proto file. However, the rest of the elements must be the same for the communication system to recognize that it's meant to be the same interface.

Some differences are tolerated (which we will talk about when we cover API versioning in Chapter 5, How to Apply Versioning to the gRPC API). But the fundamental structure of your standard gRPC element definitions must match.

Applying gRPC client components to the code

In your BasicGrpcClient project, locate the Program.cs class and change its content to the following:

using System;
using System.Threading.Tasks;
using Grpc.Net.Client;
namespace BasicGrpcClient
{
    class Program
    {
        static async Task Main()
        {
             // The port number(5001) must match the port of 
             the gRPC server.
            using var channel = 
            GrpcChannel.ForAddress("https://localhost:5001");
            var client = new 
            GreetingsManager.GreetingsManagerClient(channel);
            var reply = await client.GenerateGreetingAsync(
            new GreetingRequest { Name = "BasicGrpcClient" });
            Console.WriteLine("Greeting: " + reply.
              GreetingMessage);
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Please note that the highlighted URL represents the HTTPS access point to the gRPC server. This will not be available if you are running your software on Mac. The workaround to this is described in the Running a gRPC service on Mac section of this chapter.

This code does the following:

  1. First, it initializes the gRPC channel for the hardcoded address of https://localhost:5001. Please note that this is the same address that we defined in the launchSettings.json file in the BasicGrpcService project. But in a real commercial application, this will be configurable rather than hardcoded.
  2. Then, it uses this channel to initialize a new instance of the gRPC client that was generated by the GreetingsManager service definition in our greeter.proto file.
  3. Next, it calls the GenerateGreetingAsync method of the client object with a new instance of GreetingRequest that has its Name property set to BasicGrpcClient. Please note that it represents the GenerateGreeting rpc definition from the proto file, but the Async part has been added to the name. This is because, in .NET, each gRPC procedure is represented by synchronous and asynchronous methods on the client side. The async version returns an awaitable task, so the calling code can be set to do something else while we are waiting for the reply. The synchronous version, which has the same name as the original rpc definition but without "Async" at the end, blocks execution of the calling code until the result has been received.
  4. From this call, we receive an instance of GreetingResponse.
  5. Then, we read the value of its GreetingMessage field and print it in the console.
  6. Finally, the console prompts the user to press any key to exit.

Now, you can launch both of your applications and see how they communicate with each other. It's better to launch the server application first to make sure that it has fully loaded before the client application tries to communicate with it.

The simplest way to launch both applications is to open two instances of the command-line window (cmd, PowerShell, or Terminal, depending on your operating system and your preferences). In one command-line window, navigate to the BasicGrpcService project folder and execute the following command:

dotnet run

This will build and run the server application for you. Once it's showing the output that indicates that the gRPC server is running, open the other instance of the command-line window, navigate to the BasicGrpcClient project folder, and execute the same command.

You should receive the following output, which indicates that the client was able to successfully call the method on the server via the network:

Figure 1.10 – Console output from the gRPC client application

Figure 1.10 – Console output from the gRPC client application

Now, if you re-examine the code from your client and your server, you will see that it looks almost as if you are calling the code from inside the same application. And that's precisely what makes gRPC so easy to use.

In both the applications that we covered, you saw how relevant code is automatically generated from proto files. In certain scenarios, it would be useful to know how this mechanism works. This is what we will have a look at now.