DEV Community

loading...
Cover image for Intro to gRPC - Step by Step

Intro to gRPC - Step by Step

moe23 profile image Mohamad Lawand ・8 min read

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

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
Enter fullscreen mode Exit fullscreen mode

Configure SSL trust

dotnet dev-certs https --trust
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Once you save this file, now we need to add it to the csproj file

<ItemGroup>
  <Protobuf Include="Protos\customers.proto" GrpcServices="Server" />
</ItemGroup>
Enter fullscreen mode Exit fullscreen mode

Now we need to build our application

dotnet build
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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>();
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

reference: https://docs.microsoft.com/en-gb/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#unable-to-start-aspnet-core-grpc-app-on-macos

Create Client Application

dotnet new console -o GrpcGreeterClient
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}");
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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 {

}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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 {

}
Enter fullscreen mode Exit fullscreen mode

now we need to build the application

dotnet build
Enter fullscreen mode Exit fullscreen mode

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}");
  }
Enter fullscreen mode Exit fullscreen mode

Thank you for reading, please share and follow me for more content like this

Discussion (0)

pic
Editor guide