gRPC: The Architecture and Implementation Flow

A Deep Dive into gRPC: The Architecture and Implementation Flow

A Deep Dive into gRPC: The Architecture and Implementation Flow

You've seen gRPC in action in our previous exercises, but have you ever wondered what's happening under the hood? This post will provide a comprehensive overview of gRPC, from its core concepts to the architectural flow, and compare it to the more familiar REST API model. We'll break down the purpose of each file and component to give you a complete picture.


🧠 Part 1: Conceptual Overview – How gRPC Works

**gRPC** stands for **Google Remote Procedure Call**. At its core, it's a modern, high-performance framework for building APIs. The fundamental idea is to make a remote call to a method on a server feel just like calling a local method in your code.

The process works like this:

  1. You start by defining your API contract in a `.proto` file. This is the single source of truth for your service.
  2. Using gRPC tools, this `.proto` file is used to **generate strongly-typed code** for your chosen language (like C#). This includes:
    • The message types (the data structures for input and output).
    • A service interface or base class for the server to implement.
    • A client stub that the client uses to make the calls.
  3. The server then **implements the service** by providing the business logic for each method defined in the `.proto` file.
  4. The client uses the generated stub to **call the server**, as if it were a local method. The gRPC framework handles the complex network communication for you.

The communication itself is highly efficient, using **HTTP/2** as its transport protocol. This enables powerful features like **streaming**, **binary message payloads** (using Protocol Buffers), and **multiplexing** (sending multiple requests over a single TCP connection), which results in significantly better performance than traditional REST APIs over HTTP/1.1.


⚙️ Part 2: gRPC Architecture — Diagram and Layers

Comparing the architectural flow of gRPC to a REST API provides a clear understanding of their differences.

🔄 gRPC Architecture

The gRPC client makes a direct method call on a generated stub, which handles all the serialization and network communication using HTTP/2.


Client
 └── GreeterClient.HelloAsync()   <-- auto-generated stub
     └── HTTP/2 request (binary ProtoBuf)
         └── ↓
             SERVER
             ├── GreeterService : Greeter.GreeterBase
             └── Implementation of Hello()

Shared Language: hello.proto
        

🌐 REST API Architecture

The REST client makes an HTTP request to a URL endpoint. The server then has to parse this text-based request and route it to the correct controller method.


Client
 └── HttpClient.GetAsync("api/hello")
     └── HTTP/1.1 request (JSON)
         └── ↓
             SERVER
             └── [HttpGet("api/hello")]
                 public IActionResult Hello()
        

🔍 Part 3: Key Differences Between REST and gRPC

Here's a table summarizing the key distinctions between the two technologies:

Feature REST API gRPC
**Transport Protocol** HTTP/1.1 HTTP/2
**Payload Format** JSON (text-based) Protobuf (binary)
**Contract Definition** Implicit (or optional via Swagger/OpenAPI) Explicit and required via `.proto` file
**Code Generation** Mostly manual (DTOs, controllers) Automatic from `.proto` file
**Performance** Slower (text-based, more overhead) Faster (binary, compact, multiplexed)
**Streaming Support** Limited (Server-Sent Events, WebHooks) Built-in (Client, Server, Bi-directional)
**Browser Friendly** Yes (native in browsers) Not directly (needs gRPC-Web)

🧱 Part 4: Significance of Each File in a gRPC Implementation

1. `hello.proto` – The gRPC Contract File

This is the most important file in your gRPC solution. It defines the service contract: the methods, the input types, and the output types. This single file is used by both the server and the client to ensure they are speaking the same language.


syntax = "proto3";

// Defines the service with one method
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// Defines the input message
message HelloRequest {
  string name = 1;
}

// Defines the output message
message HelloReply {
  string message = 1;
}
        

2. Generated Files (e.g., `Greeter.cs`)

When you build your project, the `Grpc.Tools` package automatically runs the Protobuf compiler to generate C# code from your `.proto` file. This code includes:

  • The message classes (`HelloRequest`, `HelloReply`) with all the necessary serialization/deserialization logic.
  • A `GreeterClient` class, which is a stub for the client to use.
  • A `Greeter.GreeterBase` class, which is the base class your server service will inherit from.

**Important:** You should never manually edit these files as they are regenerated on every build.

3. `GreeterService.cs` (on Server)

This is where you write the business logic. Your class inherits from the generated base class and overrides the service methods.


public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply { Message = $"Hello, {request.Name}" });
    }
}
        

4. `Program.cs` (on Server)

This file is responsible for registering the gRPC services with the ASP.NET Core dependency injection container and mapping the incoming gRPC calls to the correct service implementation.


builder.Services.AddGrpc();

app.MapGrpcService<GreeterService>();
        

5. Client Code (e.g., in a Blazor component)

The client code uses the generated client stub to make the remote call. The gRPC channel handles the connection to the server.


var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = "Raushan" });
Console.WriteLine(reply.Message);
        

🧩 Summary: The gRPC Communication Lifecycle

Putting it all together, here is the full lifecycle of a gRPC call:

  1. **Define `.proto` file:** You create the service contract.
  2. **Generate C# code:** The Protobuf compiler generates the necessary classes.
  3. **Server implements:** You write your business logic in a service class.
  4. **Register service:** You configure the server to listen for gRPC calls.
  5. **Client calls:** The client uses the generated stub to make the remote call.
  6. **gRPC handles it:** The framework takes over, handling all the binary serialization, HTTP/2 transport, and execution of the service method on the server.

This clear, contract-first approach with automated code generation is why gRPC is becoming a popular choice for high-performance microservices communication.

Raushan Ranjan

Microsoft Certified Trainer

.NET | Azure | Power Platform | WPF | Qt/QML Developer

Power BI Developer | Data Analyst

📞 +91 82858 62455
🌐 raushanranjan.azurewebsites.net
🔗 linkedin.com/in/raushanranjan

Comments

Popular posts from this blog

Blazor: Building Web Apps with C# - Introduction

Blazor WebAssembly Hosted App Tutorial: Envelope Tracker System

Securing MVC-based Applications Using Blazor