If you want to explore the sample code for this post you can find it on my GitHub account.
What is gRPC?
gRPC is a schema-first framework initially created by Google. It supports service to service communication over HTTP/2 connections. It uses the Protobuf wire transfer serialisation for lightweight, fast messaging between the services. As more and more of us build interconnected microservices it can sometimes be a pain using REST as our protocol over HTTP for such purposes. REST was designed for human-readable data transfer and often results in a fair amount of boiler-plate code on both the client and server services to implement.
gRPC allows us to define our message schema and “contract” up front using Protocol Buffers. This file is then used to automatically generate much of the client and server code on our behalf. It is an interesting technology and looks well placed to become a popular choice when working with microservices. For my hack day project, I wanted to take it for a quick spin and see if I could get a client and server working over gRPC.
NOTE: The information in this post is therefore based on early code and has the potential to change during the remaining previews and after release. If you’re reading this in the distant future you may want to look for updated blog posts from me!
Microsoft is actively contributing to the “gRPC for .NET” repository to support gRPC in the ASP.NET Core 3.0 timeframe. Currently, this is a work in progress, there is no formal NuGet package available and it depends on some features not yet available in the current ASP.NET Core 3.0 preview 2 release.
To support working with the newest bits, I found that I first needed to download the latest “preview 3 “daily build using the links provided on GitHub.
With the SDK installed, I created a solution in the Visual Studio 2019 preview to begin hacking around. After cloning the grpc-dotnet repo I copied the code from the Grpc.AspNetCore.Server folder into a .NET Core 3.0 library project. As there’s no NuGet package that I have found so far this was the quickest way to get up and running.
Update: There is actually a NuGet package available which we can reference to avoid the step of manually adding the Grpc.AspNetCore.Server code. I’ll update the sample csproj files to reflect this below.
My hack day project was then heavily based off of the gRPC samples in the repository.
The sample “domain” for my hack was to develop a basic client data API which would be exposed as a gRPC server.
Creating the Protobuf Service Descriptor
gRPC uses a .proto (Protobuf) file to define the shape of your service contract. This contains the “schema” for the messages that will be sent between the exposed services.
My initial proto file looks like this:
For now, I’ve defined a single RPC service interface called GetProperty. This accepts a PropertyRequest and returns a PropertyReply which are defined underneath the service.
Messages are defined using scalar types by providing the type, a name and then a unique number for the field. These uniquely identify the fields as they’ll appear in the binary message format and should not change once defined.
For example, my PropertyRequest message has a single string value called “propertyId” and I’ve assigned it a value of 1.
Creating the Server
The next step is to create an ASP.NET Core 3.0 server which will use code automatically generated from the proto file to reduce the amount of code we need to add. We can stub out the functionality over the generated code, which handles the serialisation and communication.
I created a standard ASP.NET Core 3.0 API project and then updated the csproj file in line with the available example in the grpc-dotnet repo.
Without diving too deep this references the .proto file and the Grpc.Tools library in order to support the code generation work.
The next step is create a class derived from the generate code base class which once completed looks like this:
You can see the using statement in line 4 which matches the name of the package as defined in the proto file.
A static class ClientProperty has been generated which includes a sub-class called ClientPropertyBase which is what we derive from.
I can then override the base GetProperty method, which is so named because the proto file defines a service which includes that interface. It accepts a PropertyRequest type and returns a Task<PropertyReply>. Both of these have been code generated for me based on the proto file.
The only code I need to add here is the logic for fulfilling the request. In this sample, I have an in-memory store of some basic test data which I query using MediatR. It’s not too important how those bits work and you can code this however you need.
The program.cs for this API looks like this:
This using the new ASP.NET Core 3.0 generic host flow to register a web host which uses Kestrel. Kestrel is set up to listen on HTTP2 on port 50051.
The final bit I’ll show is the Startup class which looks like this:
This calls the AddGrpc extension on the IServiceCollection, available for now due to the Grpc.AspNetCore.Server code I copied in. Later this will be built in a NuGet library.
It then uses the new ASP.NET Core 3.0 routing middleware to map a GrpcService route to the PropertyService. This will allow the server to handle the gRPC requests.
Creating the Client
To send request to the server I created a basic .NET Core console app client. The csproj file for this also had a reference to the proto file so that the client code could be generated.
The only code needed lives in the Main method in this sample:
First we establish a gRPC channel to the server. For this sample I’m using insecure communication. I’ll show how SSL can be applied in a future post.
I then create a ClientPropertyClient which is a class created by the code gen process based on the proto file.
I can then call methods on the client which map to the methods as defined in the proto file. I simply create a PropertyRequest object (again, the class is from generated code) and send it via the client.
Here I write out the values from the PropertyResponse object to the console. Good enough for now!
I also handle a RpcException which is thrown if the server sends a NotFound status for example. I’ve not explored this too deeply so there may be better approaches to achieve this.
And that’s basically it. I can fire up the server and then fire the client up which will send a request over HTTP2 and gRPC. Awesome!
This has been a high-level overview of gRPC, after I’ve only spent a few hours learning how it works. I hope it serves to show one of the big benefits of the gRPC proto file which is the fact that we have to write very little boiler-plate code. There are no Controllers to define and even the models can be easily generated for both the client and server. On the client side, I don’t need to construct HTTP requests and fire them. I can simply make a call to a code generated client method, passing a simple request message.
As part of my hack I’ve also spent some time trying out the streaming capabilities of gRPC and working with a basic Node.js client. I’ll explore these in more detail in future posts.