Back to All Posts

The Next Step in Microservices – Using gRPC with .NET Core 3.0

This article is written by Irina Scurtu, Microsoft MVP for developer technologies, software architect, CTT+ technical trainer and Microsoft certified trainer. She is recognized globally as a .NET speaker and trainer.

Following a long partnership with Fortech, Irina is sharing with us in this guest blog post her take on using gRPC with .NET Core 3.0 in the microservices architecture. We are excited to support her in inspiring our .NET colleagues to persevere and write outstanding .NET applications.

 

A short history of RPC

Distributed systems primarily appeared because we needed to support an increasing load. Also, we needed the ability to quickly scale the application, without spending lots of money on more performant servers.

With scalability and costs in mind, it was clear that not all parts of the application should be scaled. Thus, new ways to transfer data from one application to another were researched.  The Remote Procedure Call (RPC) was a good candidate.

RPC has been around since 1970. As a technology, it makes a request to a remote network service that looks the same as calling a local method belonging to the same process. When it comes to microservices, it might be a good enough choice, because it has a lower learning curve compared to messaging systems.

Because the code looks like it is from the same component, the development teams might forget that there is a network involved and that the communication is not reliable. Besides this, we also introduce a not so obvious runtime coupling that can hurt performance and costs.

 

What is gRPC

gRPC is an evolution of RPC, a language-agnostic Remote Procedure Call framework, that uses Protocol buffers as Interface Definition Language and message format. It was developed by Google with a keen focus on performance, low latency, and high throughput.

Protocol buffers (protobuf) are a language-neutral, platform-neutral, extensible way to serialize data for communication purposes and data storage. They encode data in a binary format.

gRPC comes with a code generation tool that takes a schema definition as input and generates code as an output.  This generated code can serve as a base for services, can have domain logic on top of it, and the application can call it to encode or decode records. This removes the need to manually implement parsing or write serialization or deserialization code for class types that are used by those services.

Protocol buffer’s schema is a file with .proto extension. It allows you to define services and the operations supported along with the message format used by the client app and transferred through the network.

The schema language is much simpler than XML or JSON schema, is self-describing, and also, because it is required for decoding, a very valuable form of documentation.

Using such a schema will allow developers to introduce new fields in the contracts without breaking consumer applications. You can configure new versions to be backward compatible, or the old ones to be forward compatible, giving you the same flexibility level as a schemaless database, without much hassle.

In addition, gRPC uses HTTP/2 as a communication protocol which adds all the nice flavors. Among these, we can find the HTTP header compression and multiplexed requests. Also, it supports different connection options like unary, server-side streaming, client-side streaming or bi-directional streaming.

Even though it is not mature enough or supported by all the browsers, HTTP/2 can have multiple concurrently opened streams over a single connection. This reduces the numbers of DNS lookups and bottlenecks associated with creating new TCP connections.

These files are written in a specific language named Protocol Buffer. This language is very simple, similar to TypeScript, or JavaScript and very easy to understand.

Once these files are compiled using the protoc compiler, you can target 10+ different programming languages like C#, Java, Ruby or many others. The output of the compilation will be an interoperable, high-level service definition (in our case in C#), and the message types supported by it.

Protocol buffers are 3-10 times smaller and 20-100 times faster than XML or JSON and give you a reduced network usage due to the performant binary serialization.

In microservices architectures, simple HTTP calls are the go-to choice to do point to point communication between systems. But, by changing a contract on one side, you might break the other.

However, by using Protocol buffers, you’ll have a single common contract for both applications, that can be forward or backward compatible, self-describing and payload agnostics.

Protocol Buffers allow developers to avoid the need for hand-parsing, or to write a lot of code to serialize or deserialize some messages.

 

.proto files message structure

The messages defined in the .proto files are binary encoded with Protobuf. This method is very efficient and performant, but it has the format not so “human-readable” as in HTTP.

The .proto file description will allow you to describe the request and response message format and the service definition.

syntax = "proto3";

option csharp_namespace = "MyFirstGrpc";

package Fibonacci;

// The service definition.
service Fibo {
	rpc ComputeFibonacci(RequestedNumber) returns (FibonacciResult){}
}

//the request message format
message RequestedNumber {
	int32 number = 1;
}

//the response message format
message FibonacciResult {
	int32 result = 1;
}

 

Why use protocol buffers? Or the .NET alternative

Protocol Buffers and gRPC are recommended for a variety of scenarios like:

  • Microservices – service to service calls – where you’ll have low latency and high throughput because serialization is efficient and performant where you really need efficiency;
  • Communication between systems written in different programming languages;
  • Point-to-point real-time communication: it supports bi-directional streaming and gRPC services can push messages in real-time without polling.

gRPC still has limited browser support because it uses HTTP/2 as underlying feature heavily.

Also, it is not mature yet, but it makes the perfect candidate for scenarios where the browser is not involved.

 

gRPC in ASP.NET Core

To get started with gRPC in ASP.NET Core, you need to have the latest .NET Core 3.0 SDK installed.  Make sure that, if you have Visual Studio 2019 installed, under Tools/Options you have checked the ‘allow preview SDKs’.

By following these steps, you will install the gRPC project template in Visual Studio and have a starting point.

 

Create a New ASP.NET Core Web Application with gRPC Service | Fortech

 

You should have a small sample project that is familiar, even if is new. We can use it to create a client for it.

 

gRPC Protos

 

But we will add our own proto file(math.proto) that will contain the definition of a service that will compute the Fibonacci number.

syntax = "proto3";

option csharp_namespace = "MyFirstGrpc";

package Fibonacci;

// The service definition.
service Fibo {
	rpc ComputeFibonacci(RequestedNumber) returns (FibonacciResult){}
}

//the request message format
message RequestedNumber {
	int32 number = 1;
}

//the response message format
message FibonacciResult {
	int32 result = 1;
}

Then you’ll need to make sure you edit the .csproj file to include your new .proto file. This will allow the compiler to generate a base class for your service definition.

 

math.cs - .proto file

gRPC Services - Protobuf Include Protos\math.proto

 

Then, we will create a FibonacciService that inherits from Fibo.FiboBase. The base service comes from our .proto file, and we can build on it, providing an actual implementation. This is because it is generated as abstract and it allows you to add additional logic.

  public class FibonacciService : Fibo.FiboBase
    {

        public override Task ComputeFibonacci(RequestedNumber request, ServerCallContext context)
        {
            int n = request.Number;

            return Task.FromResult(new FibonacciResult
            {
                Result = Fibonacci(n)
            });
        }

        private static int Fibonacci(int n)
        {
            if (n == 0 || n == 1)
            {
                return n;
            }

            return Fibonacci(n - 1) + Fibonacci(n - 2);
        }

    }

Next, we will need to create a client using the same .proto file. We begin by creating a new console, copying the math.proto file and installing 3 NuGet Packages:

  • Google.Protobuf
  • Grpc.Core
  • Grpc.Tools

Then, we need to edit the properties of our math.proto file to make sure that is compiled as a client. We do this by selecting the appropriate build action that will make the contract discoverable and usable.

Using this contract will be possible without referencing the Server project and introducing a code dependency.

 

math.proto File Properties

 

class Program
    {
        static void Main(string[] args)
        {
            Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);

            var client = new Fibo.FiboClient(channel);
            while (true)
            {
                String number = Console.ReadLine();
                int n = Int32.Parse(number);
                var reply = client.ComputeFibonacci(new RequestedNumber { Number = n });
                Console.WriteLine($"Fibonacci for {n} is:{reply.Result}");
            }
        }
    }

 

gRPC Calls

 

Using gRPC in .NET Core was possible before, but with a lot of manual work. Now, with the shipping of gRPC with ASP.NET Core 3.0, it becomes a first-class citizen and you have all the tooling in Visual Studio.

 

gRPC in microservices

The Single Responsibility Principle should apply not only to your codebase but also to your architecture. Domain-driven design forces you to separate business capabilities into independent bounded contexts. With a clear distinction between them, these bounded contexts should not interfere with each other and have no dependencies in terms of code-sharing.

As a practice, if you have these bounded contexts in the same solution or component, the risk of creating dependencies increases significantly because are easily accessible.  When you use physical boundaries to enforce clean models, you ensure the integrity of these contexts with a ‘share-nothing’ principle.

When using gRPC, the .proto files can be ‘shared’ between contexts by providing them as artifacts in a build system. But, in the end, your components will have a clearer boundary and will communicate with each other using a well-defined contract trough known-protocols.

Having a system of composed verticals will diminish the cases when you introduce cascading breaking changes.

 

Conclusion

If you want to take the next step in your microservice architecture, you should definitely take into consideration using gRPC with .NET Core 3.0. Doing this will add all the speed and performance .NET Core has while allowing you a flexible enough design.

Also, if you plan to give Blazor a try – now that is not an experimental project anymore – and create a fully .NET SPA application, gRPC will make the component communication easier, with a cross-platform bonus.

Choosing RPC or gRPC can make non-functional requirements like scalability a little harder to achieve. It doesn’t have all the pros of REST APIs, but even so, there are a lot of big companies with huge scalability requirements that still use it as an integration strategy.

gRPC will win the battle with simple HTTP calls with a few milliseconds due to the underlying protocol, better serialization (protocol buffer) and you’ll end up with a smaller payload to transport over the network. This is not necessarily a game-changer, but it gives you the option not to share-code.

About the Author

Irina Scurtu - Guest Blogger Fortech

Irina Scurtu

Microsoft MVP for Developer Technologies, Software Architect, CTT+ technical trainer and Microsoft Certified Trainer, always in a quest for latest trends and best practices in architecture, .NET and the world around it. She is an international speaker and trainer, often writing articles on her blog.

Irina has more than 800 hours of delivered training sessions, workshops, and presentations, being passionate about architecture and microservices with all their ups and downs. She is the founder of DotNet Iasi User Group where she tries to gather people that are willing to share their knowledge and inspire others.

Your email address will not be published. Required fields are marked *