gRPC is an open-source, high-performance RPC framework. In this blog, I am going to cover gRPC in ASP.Net Core. We can use gRPC within or across data centers. We can use gRPC in mobile applications, web applications as well as edge computing as a backend service.
Google was the first to develop gRPC, later it open-sourced it. gRPC uses HTTP 2 for the transport protocol. It uses protobuf (or protocol buffer) for defining interfaces. gRPC is a contract first framework, allowing us to build contract first based distributed systems.
When it comes to the analogy, gRPC is more close to Remoting than compared to REST Services. In gRPC client can make a server method call, as if the method is in the local object. This makes it really easy to create distributed systems. Whereas REST services are based on resource access.
We can build gRPC clients and servers in completely different programming languages. Meaning, the server can be in C#, whereas the client can be in Python.
By default gRPC uses protobuf for serializing data, however, it can be using JSON format as well.
gRPC was introduced in .Net Core with the release of .Net Core 3.0. Hence you will need .Net Core 3.0 installed for gRPC to work.
The five main features of gRPC
Firstly, it supports a simple service definition. We define The services using protobuf (or protocol buffer). Protobuf (or protocol buffer) is a language-agnostic, platform-agnostic mechanism for serializing data (Similar to JSON or XML). It is extremely faster compared to the XML or JSON serializer.
Secondly, very quick to start and scales. Setting up a development environment and starting to code is extremely simple and fast. Also, the framework can handle millions of gRPC calls per second.
Thirdly, gRPC works across languages and platforms. The framework creates autogenerated classes across languages. Wich makes it very easy to get started.
Fourthly, it supports bi-directional streaming across services.
Finally, authentication is fully integrated with the gRPC framework.
Four types of remote procedure call in gRPC
Firstly unary remote procedure call. In this, the client sends a single request to the server. And gets a single response back from the server.
Secondly, the server streaming remote procedure calls. In this case, the clients sends a single request to the server. And gets a stream of data as a message back from the server.
Thirdly, the client streaming remote procedure calls. In this case, the client sends the request as a stream of data to the server. And server waits for the complete stream, and finally returns response to the client.
Fourthly, bi-directional streaming remote procedure calls. In this case, clients sends a stream of request to the server. And on the other side the server sends a stream of response back to the client. The server might choose to wait for the entire request stream, or can act on one message at a time. This depends on the business implementation.
Creating gRPC in ASP.Net Core
For creating a gRPC server in ASP.Net Core, firstly, I am going to create a new ASP.Net Core Web application in Visual Studio 2019 (You can use other versions of Visual Studio as well. As long as it supports .Net Core 3.0).
I will select “Empty” project template. And I will keep the “Configure for HTTPS” checkbox on in the Advanced settings section.
Once the project is created, I am going to create a new folder inside the project and name it as Protos. I will keep all the gRPC contract files inside of this folder. The folder name Protos is a standard everyone uses, but it can have any name. Also, the contract files can be anywhere in the project as long as we configure those properly. And I am going to discuss the configuration soon.
Installing necessary NuGet packages
For gRPC in ASP.Net Core to work, we will install all the necessary NuGet packages. We will install the following packages:
Grpc.Tools
Grpc.Core
Grpc.AspNetCore
The Grpc.Tools
is the package which will create the autogenerated code from .proto
files. It reads the ItemGroup
configuration from the project file for identifying the gRPC service definitions.
Creating .proto file for gRPC contract
As I mentioned above, protobuf is used as the language for the gRPC service contracts. And the extension for protobuf file is .proto
. Hence I will create the contract for my project as well in a .proto
extension file.
I will build a service that will return the population of a state based on the name of the state. Hence I will create a population.proto
file inside of the Protos folder.
Once the file is created I will add the following code inside the .proto
file.
syntax = "proto3";
option csharp_namespace = "GrpcPopulation";
service PopulationProvider {
rpc GetPopulation (PopulationRequest) returns (PopulationResponse);
}
message PopulationRequest{
string State = 1;
}
message PopulationResponse{
string Count = 1;
}
Let us go through each of the line items in the above file.
The first line defines the version of the protobuf we are going to use. For gRPC the recommendation is to use version 3 or proto3.
The second line defines the namespace of the autogenerated class created by the gRPC framework. This line is not necessary but helps if we want to isolate the autogenerated class from the rest of the code.
The third line defines the remote procedure. Inside the service definition, we will put the RPC method that we want to expose. The syntax is straight forward, it starts with rpc
, followed by the method name, then the request type then returns
and finally the response type.
The next two line defines the request and response type. The number after the property is the position of the property. For example, in the PopulationRequest
type, the string State = 1
, means the position of the State
property is first.
Update Server Project file
Once we finish defining the population.proto
file, it is time to update the project file. We will add a new ItemGroup section in the project file for configuring .proto
files.
<ItemGroup>
<Protobuf Include="Protos\population.proto" GrpcServices="Server" />
</ItemGroup>
In the above ItemGroup
section, Include
attribute defines the location of the gRPC service definition protobuf files. And the GrpcServices
attribute defines if it is a server or client application. The GrpcServices
attribute can have either Server
or Client
as the possible values.
Once the server project is configured, I will build the application. This will create the autogenerated gRPC files necessary for the server code.
Creating gRPC service
Next, I will create a new class PopulationService
. This class will contain the gRPC method for clients to call. This class will derive from PopulationProvider.PopulationProviderBase
class, which is the autogenerated class from the population.proto
file.
Inside of this class, I will override the GetPopulation
method. This is the method I defined inside of the population.proto
file is the remote procedure.
The implementation of the GetPopulation
will be simple. For the time being, I will return a hardcoded value of 1000
for the population count irrespective of the input request state.
using Grpc.Core;
using GrpcPopulation;
using System.Threading.Tasks;
namespace GrpcDemo.Server
{
public class PopulationService : PopulationProvider.PopulationProviderBase
{
public override Task<PopulationResponse> GetPopulation(
PopulationRequest populationRequest,
ServerCallContext serverCallContext)
{
return Task.FromResult(new PopulationResponse { Count = "1000" });
}
}
}
Update Startup
Once the service class is created, it is time to configure gRPC in the ASP.Net core pipeline.
To Achieve this, firstly I will update the ConfigureServices
method inside of the Startup
class to add gRPC.
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
Secondly, I will update the Configure
method inside of the Startup
class. Inside of the Configure
method, I will map the gRPC endpoint.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
app.UseEndpoints(e => e.MapGrpcService<PopulationService>());
}
At this point the service is ready. I will start the service.
In the console log of the gRPC server, you can see the following line “Added gRPC method ‘GetPopulation’ to service ‘PopulationProvider’. Method type: ‘Unary’, route pattern: ‘/PopulationProvider/GetPopulation’“. This implies that we are running the first RPC type supported by gRPC. It is the unary RPC.
Creating Client for gRPC in ASP.Net Core
Now that the server is up and running, it is time to create a gRPC client. For the client, I will create a new .Net Core Console application using Visual Studio 2019.
Once the project is created, I will create a new folder named “Protos” inside of the project. As I mentioned before, gRPC is a contract based framework. Hence I will copy the same population.proto
file which is defined in the server inside of the Protos folder.
Installing NuGet packages
Just as we did for the server, we will need to install gRPC related NuGet packages in the client as well. There are a couple of common packages, but the client also needs Grpc.Net.Client
, which is unique to the client.
I will install the following NuGet packages:
Grpc.Tools
Grpc.Core
Grpc.Net.Client
Google.Protobuf
Update Client Project file
Once we finish installing the NuGet packages, it is time to update the project file. I will add a new ItemGroup
section in the project file for configuring .proto
files just as I did for the server.
The only difference is that for the GrpcServices
attribute, instead of Server
, here I will set Client
for the value.
<ItemGroup>
<Protobuf Include="Protos\population.proto" GrpcServices="Client" />
</ItemGroup>
Calling gRPC service
Once the client project is also configured, I will build the application. This will create the autogenerated gRPC files necessary for the client code.
Next, inside of the Main
method, inside of the Program
class, I will create the gRPC channel. I will use GrpcChannel.ForAddress
to create a new gRPC channel. And I will pass the localhost
address of the gRPC server to the ForAddress
method.
var channel = GrpcChannel.ForAddress("https://localhost:5001/");
Once the channel is created, I will create a gRPC client. And for creating the client, I will create a new instance of PopulationProviderClient
, which is an autogenerated class from the population.proto
file.
var client = new GrpcPopulation.PopulationProvider.PopulationProviderClient(channel);
And you can notice in the above code, the namespace is GrpcPopulation
. Which is what I provided in the population.proto
file.
Finally, I will call the GetPopulation
method on the server to get the population of New Jersey (tough in server irrespective of state it will always return 1000 as population).
var population = client.GetPopulation(new GrpcPopulation.PopulationRequest { State = "NJ" });
The complete client code is below.
using Grpc.Net.Client;
using System;
namespace GrpcDemo.Client
{
class Program
{
static void Main(string[] args)
{
var channel = GrpcChannel.ForAddress("https://localhost:5001/");
var client = new GrpcPopulation.PopulationProvider.PopulationProviderClient(channel);
var population = client.GetPopulation(new GrpcPopulation.PopulationRequest { State = "NJ" });
Console.WriteLine(population.Count);
}
}
}
Once I run the client code, I can see the response 1000 in the output.
gRPC and Dependency Injection
gRPC supports Dependency injection in ASP.Net core just like any other features. As long as we configure a type in the dependency injection container, it will be available in the gRPC service.
To demonstrate this, I will create a new interface called IStatePopulationProvider
. This interface will have a single method Get
.
namespace GrpcDemo.Server
{
public interface IStatePopulationProvider
{
long Get(string state);
}
}
Once the interface is defined, I will create an implementation class StatePopulationProvider
. This class will keep an in-memory dictionary of the state name and its population.
using System.Collections.Generic;
namespace GrpcDemo.Server
{
public class StatePopulationProvider : IStatePopulationProvider
{
private readonly IDictionary<string, long> states = new Dictionary<string, long> {
{ "NJ", 10000 },
{ "NY", 20000 },
{ "MD", 30000 },
{ "KY", 40000 },
};
public long Get(string state)
{
if (states.ContainsKey(state))
{
return states[state];
}
return 0;
}
}
}
After the StatePopulationProvider
class is implemented, I will update PopulationService
to use IStatePopulationProvider
. Hence, instead of a constant population count, PopulationService
will use IStatePopulationProvider
instance to get population count by state name.
using Grpc.Core;
using GrpcPopulation;
using System.Threading.Tasks;
namespace GrpcDemo.Server
{
public class PopulationService : PopulationProvider.PopulationProviderBase
{
private readonly IStatePopulationProvider statePopulationProvider;
public PopulationService(IStatePopulationProvider statePopulationProvider)
{
this.statePopulationProvider = statePopulationProvider;
}
public override Task<PopulationResponse> GetPopulation(
PopulationRequest populationRequest,
ServerCallContext serverCallContext)
{
var count = statePopulationProvider.Get(populationRequest.State);
return Task.FromResult(new PopulationResponse { Count = count.ToString() });
}
}
}
Once this is complete, I will configure the IStatePopulationProvider
and its implementation class StatePopulationProvider
in the Startup
class. I will update the ConfigureServices
method to register StatePopulationProvider
in the dependency injection container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IStatePopulationProvider, StatePopulationProvider>();
services.AddGrpc();
}
Once everything is complete I will start the server. And then I will run the client code to test everything up. And the result will be as expected.
Conclusion
Creating an application to run gRPC in ASP.Net Core is as simple as it can get. The NuGet packages provided by gRPC does all the heavy lifting. We just need to take care of creating the .proto
files.
Github repo: https://github.com/choudhurynirjhar/gRPC
YouTube video: https://youtu.be/OhijduW7jBA
References:
https://docs.microsoft.com/en-us/aspnet/core/grpc/basics?view=aspnetcore-3.1