In this article will explaining to you what is gRPC, what's the difference between gRPC and Rest and how we can use it in our applications.
You can watch the full video on Youtube:
You can also find the full source code:
https://github.com/mohamadlawand087/v17-IntroTogRPC
So the Agenda for today:
- What is gRPC
- gRPC vs REST
- The structure of our application
- What do we need to get started
- Code
If you find this video helpful, please like, share and subscribe as it will really help the channel.
Before we start you will need to have
- Dotnet SDK (https://dotnet.microsoft.com/download)
- Visual Studio Code (https://code.visualstudio.com/)
Links will be available in the description down below
What is gRPC
gRPC is a modern high performance way to communicate between applications
its a popular open source Remote Procedure Call framework, RPC frameworks makes it easy for Apps to talk to each other.
gRPC is built on
- HTTP/2 which is its transport protocol layer
- Protobuf which is gRPC serialisation technology and language neutral contract language
gRPC is designed for modern Apps specifically MicroServices
- Its high performance
- Platform Independent: there gRPC implementation in almost all modern programming languages
What is the difference between GRPC and REST
gRPC
- Contract First (proto file) opinionated contract first RPC framework. The contract is defined in a proto file and that file is the heart of gRPC. it defines the APIs and the messages that we are going to send. Its a language neutral way to define APIs. Because that file we past it to other languages and use it from them and based on which language we are using it drives code generation. Which will generate a strongly typed client and messages for the current platforms
- Content is binary: HTTP/2 and Protobuf are binary protocols and the content is designed for computers and high performance
- gRPC its designed to hide the complexity of remoting. gRPC library and code generation means we dont need to worry about routing and headers and serialisation. When it comes to calling a method on a client all we need to do is invoke that method and
- Performance Developer Productivity
Let us Start Coding
REST API
- Content First (URL, HTTP method, Json), we focus the readability and formatting
- They are text based HTTP 1.1 and Json, they are human readable. Which mean they are great for debugging not for performance
- Emphasises on HTTP alot more we a need to consider the low level concerns, which is good since we get alot of control over http requests
- Widest Audience every computer is able to use HTTP/1.1 and Json , Ease of getting started
Create Sample Server Application
dotnet new grpc -n GrpcService
Configure SSL trust
dotnet dev-certs https --trust
Let us open the new application in VS Code and check what we get. We can see out of the box we get
- Protos folder
- Services folder
Inside our Protos folder we have a greet.proto file, the Proto file is the Language Neutral Way to define our APIs.
We can see from the file we have a Greeter service and SayHello method. Think about the Greeter service as a controller and the SayHello method as an action.
// Specify the latest schema that we can use
syntax = "proto3";
// define the name space for this proto usually its the same as
// our Grpc Server
option csharp_namespace = "GrpcService";
package greet;
// we can think of a service as a class
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message is like a model in c#
// defining a property inside of it
// the number is for the ordering of the property
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
a message is like a Model in .net
The SayHello methods take a HelloRequest which is a message and return HelloReply which is also a message.
Inside our GreeterService file we can see we have a GreeterService which inherites Greeter.GreeterBase which is auto generated code from the proto file.
As we we have the a SayHelloMethod which is overriden since its being inherited from the GreeterBase. Within the HelloMethod we are taking a request "HelloRequest" and returning a "HelloReply" this classes are also autogenerated for us from the proto file.
Basically code generation is responsible for generating the files for us based on the proto file definition. gRPC is doing all of the heavy lifting in generating, routing and serialising the code for us. All we need to do is implement the base class and override the implementations of the method.
Lets try running our gRPC service
dotnet run
We can see from our the result in the endpoint that got created for us we cannot use gRPC as we use REST client from the web browser we need to create a gRPC client to be able to communicate with the service as gRPC requires the Proto file because its a Contract First RPC framework. Currently our web browser don't know anything about a Proto file so it doesn't know how to process request.
Now lets create our custom Proto file called customers.proto, inside our Protos folder we need to adda new file called customers.proto then we need to update it as per the below
syntax = "proto3";
option csharp_namespace = "GrpcService";
package customers;
service Customer {
rpc GetCustomerInfo (CustomerFindModel) returns (CustomerDataModel);
}
message CustomerFindModel {
int32 userId = 1; // bool, int32, float, double, string
}
message CustomerDataModel {
string firstName = 1;
string lastName = 2;
}
Once you save this file, now we need to add it to the csproj file
<ItemGroup>
<Protobuf Include="Protos\customers.proto" GrpcServices="Server" />
</ItemGroup>
Now we need to build our application
dotnet build
The next step is add our CustomerService, inside our Services folder we need to add a new class called CustomerService.cs and update its content as follow
public class CustomerService : Customer.CustomerBase
{
private readonly ILogger<CustomerService> _logger;
public CustomerService(ILogger<CustomerService> logger)
{
_logger = logger;
}
public override Task<CustomerDataModel> GetCustomerInfo(CustomerFindModel request, ServerCallContext context)
{
CustomerDataModel result = new CustomerDataModel();
// This is a sample code for demo
// in real life scenarios this information should be fetched from the database
// no data should be hardcoded in the application
if(request.UserId == 1) {
result.FirstName = "Mohamad";
result.LastName = "Lawand";
} else if(request.UserId == 2) {
result.FirstName = "Richard";
result.LastName = "Feynman";
} else if(request.UserId == 3) {
result.FirstName = "Bruce";
result.LastName = "Wayne";
} else {
result.FirstName = "James";
result.LastName = "Bond";
}
return Task.FromResult(result);
}
}
Now we need to update our Startup.cs class to inform our application that we have new Endpoint for the new Service that we have created. So inside our Configure method we need to add the below inside out app.UserEndpoints
endpoints.MapGrpcService<CustomerService>();
MacOS note:
Since Mac Os doesn't support HTTP/2 over TLS due to missing ALPN support. We need to have a workaround, for this we need to update the Program.cs with the below
webBuilder.ConfigureKestrel(options =>
{
// Setup a HTTP/2 endpoint without TLS.
options.ListenLocalhost(5000, o => o.Protocols =
HttpProtocols.Http2);
});
Create Client Application
dotnet new console -o GrpcGreeterClient
Now we need to add packages to the client console application so it will be able to recognise gRPC, lets navigate to GrpcGreeterClient
dotnet add package Grpc.Net.Client
dotnet add package Google.Protobuf
dotnet add package Grpc.Tools
Add greet.proto
1- first we need to add a folder to the client project called Protos
2- We need to copy the content of the Protos folder from the gRPC greeter service to the gRPC client project
- greet.proto
- customers.proto
3- After pasting the file we need to update the namespace for it to be
option csharp_namespace = "GrpcGreeterClient";
4- we need to update the csproj file, GrpcGreeterClient.csproj file so its aware of the proto file we copied
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\customers.proto" GrpcServices="Client" />
</ItemGroup>
This Protobuf element is how code generation know about the Proto file, we are saying here we want to have a client file.
We need to build our client and make sure everything is building successfully
dotnet run
Now lets add some code to our console application to call our server inside our Program.cs
// We create a channel it represent the connection from client to the server
// the Url that we add is provided for from Kestrel in the server
var channel = GrcpChannel.ForAddress("https://localhost:5001");
// this the strongly typed client that was create for us from code generation
// when we added the Proto file
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest
{
Name = "Mohamad"
});
Console.WriteLine("From Server: " + response.Message);
var customerClient = new Customer.CustomerClient(channel);
var result = await customerClient.GetCustomerInfoAsync(new CustomerFindModel()
{
UserId = 1
});
Console.WriteLine($"First Name: {result.FirstName} - Last Name: {result.LastName}");
Now lets add stream capabilities
This is a basic gRPC call, this is called a unary call, with that gRPC support streaming. A streaming call is when you send multiple request or responses.
Lets go back to our customers.proto and add a stream method inside out Customer service
// since we are going to return a list of customers
// we cannot return lists in gRPC we return a stream
rpc GetAllCustomers (AllCustomerModel) returns (stream CustomerDataModel);
As you can see we have added the keyword stream in the return, which means we are added a stream "multiple" replies.
as well we need to and an empty message
// in gRPC we cannot have a method with empty parameters
// so we create an empty message
message AllCustomerModel {
}
To implement this method we need to go to the Services folder and in our CustomerService we need to add the below:
public override async Task GetAllCustomers(AllCustomerModel request, IServerStreamWriter<CustomerDataModel> responseStream, ServerCallContext context)
{
var allCustomers = new List<CustomerDataModel>();
var c1 = new CustomerDataModel();
c1.Name = "Mohamad Lawand";
c1.Email = "mohamad@mail.com";
allCustomers.Add(c1);
var c2 = new CustomerDataModel();
c2.Name = "Richard Feynman";
c2.Email = "richard@physics.com";
allCustomers.Add(c2);
var c3 = new CustomerDataModel();
c3.Name = "Bruce Wayne";
c3.Email = "bruce@gotham.com";
allCustomers.Add(c3);
var c4 = new CustomerDataModel();
c4.Name = "James Bond";
c4.Email = "007@outlook.com";
allCustomers.Add(c4);
foreach(var item in allCustomers)
{
await responseStream.WriteAsync(item);
}
}
Now we need to copy the changes from the Server Customers.Proto file to the client Customers.Proto file and build it. Lets add the line below in our client application Proto file
service Customer {
rpc GetCustomerInfo (CustomerFindModel) returns (CustomerDataModel);
// since we are going to return a list of customers
// we cannot return lists in gRPC we return a stream
rpc GetAllCustomers (AllCustomerModel) returns (stream CustomerDataModel);
}
// in gRPC we cannot have a method with empty parameters
// so we create an empty message
message AllCustomerModel {
}
now we need to build the application
dotnet build
The next step is updating the program.cs to process the new stream method
var customerCall = customerClient.GetAllCustomers(new AllCustomerModel());
await foreach(var customer in customerCall.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"{customer.Name} {customer.Email}");
}
Thank you for reading, please share and follow me for more content like this
Top comments (0)