gRPC for ASP.NET Core 3.0

c# Aug 14, 2019

gRPC is a high performance RPC framework. A faster and more efficient alternative to JSON based REST services. gRPC uses HTTP/2 protocol and by default Google's protocol buffer binary serialisation format to transfer messages. This is mature and a must technology in cloud native products. I will spare you from the introduction as I know you are here to quickly start gRPC using ASP.NET Core.

I have recently upgraded a gRPC service at work (that was using .NET Core 2.2) into .NET Core 3.0 Preview 7. It was such a smooth migration. gRPC in .NET Core 3.0 has first class support by the framework just like using a MVC Controller, SignalR Hub or Razor page.

Starting from ASP.NET Core 3 gRPC has first class support by the framework. It is supported into the framework starting from Kestrel up to the new Endpoint routing. You no longer need to use a separate binary to generate code. It's as simple as building the project. An ASP.NET application can expose gRPC, MVC, WebApi, SignalR Hub endpoint etc all at the same time. It integrates seamlessly into Endpoint routing, built-in IoC container and generated logs are just like any other logs - all out of the box.

A gRPC service has 3 elements. A server, bunch of clients and a .proto file. If you are from JSON based service background then you know what I mean by server and client. What's new here is the proto file.

Protocol buffer file (protobuf) - The Contract

.proto file contains the scheme of your service endpoints and models (in protobuf's term - message). For our example we will need two RPC endpoints

  • SayHello that takes a name and returns a string
  • SayHelloToNobody that does not take any parameter and returns a string

Here is the proto file

If you can't see the code above switch to non-amp version of this post.

Each RPC required to take one message. Therefore we could add one or many properties in the HelloRequest message and assign an unique number for each property. Since protobuf is designed for speed and efficiency in mind, it only sends the number over the wire in order to identify a property.

You can read more about protobuf files in the Language Guide for proto3 format.

The proto file is then used by gRPC compilers (available for many popular language) to generate code for a Client and a Server. In order to do that, we need to put the proto file into a shared place to be accessed by both the client and server.

In a non-trivial C# project, you would create a NuGet package that would contain the generated code for Client and Server. For simplicity, we are going to put the .proto files and generated code into a separate c# project and reference it from both client and server projects.

Here is a netstandard2.0 project that references required NuGet packages and the proto file defined above.

Notice the GrpcServices="Both" (instead of Client or Server - we've asked for Both). That's why once you build the project - it will generate code for both the client (for consumption) and server (for implementation). Also, by targeting netstandard2.0 we have enabled wider compatibility from entire .NET ecosystem.

Server

The generated code defines the contract defined in the .proto file into programming language (in our case C#). We need to define the implementation. So we have a ASP.NET Core app that references the Protos.csproj.

Here is the Program (the entry-point of a c# app), Startup (convention based bootstrapping an ASP.NET Web/API) and an implementation of our DemoService (defined in the DemoService.proto above)

Once started, the gRPC service will be up in http://localhost:5000 endpoint. You can use any gRPC client like grpcurl CLI or BloomRPC GUI to test the server.

The server automatically generates logs message like this

/usr/local/share/dotnet/dotnet /Users/mustakim/dev/grpc-dotnet-post/Server/bin/Debug/netcoreapp3.0/Server.dll
warn: Microsoft.AspNetCore.Server.Kestrel[0]
      Overriding address(es) 'https://localhost:5001, http://localhost:5000'. Binding to endpoints defined in UseKestrel() instead.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://0.0.0.0:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/mustakim/dev/grpc-dotnet-post/Server
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 POST http://localhost:5000/GrpcDotNetDemoPackage.DemoService/SayHello application/grpc 
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint 'gRPC - /GrpcDotNetDemoPackage.DemoService/SayHello'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint 'gRPC - /GrpcDotNetDemoPackage.DemoService/SayHello'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 60.1197ms 200 application/grpc

Client

You are not limited to use a C# client. gRPC gives you the freedom to generate code in many popular programming language like Go, Java etc. All you need is the same proto file and the address of the server (http://localhost:5000) in our case. Here is a c# client that uses a HttpClient and and extension methods defined in Grpc.Net.Client nuget package to create a client using the generated client code in our Proto.csproj project.

Running the client (while the serve is running - of course) works as expected.

Other ways of getting the gRPC client

If the client was an ASP.NET web application (or a console application bootstrapped using .NET Generic Host), you could use IHttpClientFactory to get a HttpClient (instead of newing up) - which would be reused for improved performance.

Even better you can use IServiceCollection.AddGrpcClient extension method like this (need Grpc.Net.ClientFactory nuget package)

Then you can just request the IoC to inject DemoService.DemoServiceClient whenever you need to consume the gRPC service.

Securing your gRPC server

You would never deploy a gRPC server (or any service) without securing it with TLS, so here is a quick overview of what needs to be done.

  • In Server: Pass the server certificate (in pfx form) to kestrel in Program.cs
macOS does not support TLS over HTTP/2. So you will get an error if you try this. Alternatively you can use Docker.
  • The client need to specify the https://... endpoint and optionally pass the ca file (if the server certificate is self-signed or signed using your own certification authority).

Go clone yourself

You can find all of these source code in github
https://github.com/mustakimali/grpc-dotnet-demo

Mohammad Mustakim Ali

I'm a Software Engineer living in London, UK. My passion is to make *very fast* software with great user experience and I have just got little better on this than I was yesterday.