Building a gRPC Client in .NET

Introduction

In this article, we will take a look at how to create a simple gRPC client with .NET and communicate with a server. This is the final post of the blog series where we talk about building gRPC services.

Motivation

This is the second part of an articles series on gRPC. If you want to jump ahead, please feel free to do so. The links are down below.

Introduction to gRPC
Building a gRPC server with Go
Building a gRPC client with .NET
Building a gRPC client with Go

Building a gRPC client with .NET (You are here)

Please note that this is intended for anyone who’s interested in getting started with gRPC. If you’re not, please feel free to skip this article.

Plan

The plan for this article is as follows.

Scaffold a .NET console project.
Implementing the gRPC client.
Communicating with the server.

In a nutshell, we will be generating the client for the server we built in our previous post.


?  As always, all the code samples documentation can be found at: https://github.com/sahansera/dotnet-grpc

Prerequisites

.NET 6 SDK
Visual Studio Code or IDE of your choice
gRPC compiler

Please note that I’m using some of the commands that are macOS specific. Please follow this link to set it up if you are on a different OS.

To install Protobuf compiler:

brew install protobuf

Project Structure

We can use .NET’s tooling to generate a sample gRPC project. Run the following command at the root of your workspace. Remember how we used dotnet new grpc command to scaffold the server project? For this one though, it can simply be a console app.

dotnet new console -o BookshopClient

Your project structure should look like this.


You must be wondering if this is a console app how does it know how to generate the client stubs? Well, it doesn’t. You have to add the following packages to the project first.

dotnet add BookshopClient.csproj package Grpc.Net.Client
dotnet add BookshopClient.csproj package Google.Protobuf
dotnet add BookshopClient.csproj package Grpc.Tools

Once everything’s installed, we can proceed with the rest of the steps.

Generating the client stubs

We will be using the same Protobuf files that we generated in our previous step. If you haven’t seen that already head over to my previous post.

Open up the BookshopClient.csproj file you need to add the following lines:


<ItemGroup>
<Protobuf Include=../proto/bookshop.proto GrpcServices=Client />
</ItemGroup>

As you can see we will be reusing our Bookshop.proto file. in this example too. One thing to note here is that we have updated the GrpcServices attribute to be Client.

Implementing the gRPC client

Let’s update the Program.cs file to connect to and get the response from the server.

using System.Threading.Tasks;
using Grpc.Net.Client;
using Bookshop;

// The port number must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress(“http://localhost:5000”);
var client = new Inventory.InventoryClient(channel);
var reply = await client.GetBookListAsync(new GetBookListRequest { });

Console.WriteLine(“Greeting: “ + reply.Books);
Console.WriteLine(“Press any key to exit…”);
Console.ReadKey();

This is based on the example given on the Microsoft docs site btw. What I really like about the above code is how easy it is to read. So here’s what happens.


We first create a gRPC channel with GrpcChannel.ForAddress to the server by giving its URI and port. A client can reuse the same channel object to communicate with a gRPC server. This is an expensive operation compared to invoking a gRPC method on the server. You can also pass in a GrpcChannelOptions object as the second parameter to define client options. Here’s a list for that.
Then we use the auto-generated method Inventory.InventoryClient by leveraging the channel we created above. One thing to note here is that, if your server has multiple services, you can still use the same channel object for all of those.
We call the GetBookListAsync on our server. By the way, this is a Unary call, we will go through other client-server communication mechanisms in a separate post.
Our GetBookList method gets called on the server and returns the list of books.

Now that we know how the requests work, let’s see this in action.

Communicating with the server

Let’s spin up the server that we built in my previous post first. This will be up and running at port 5000.

dotnet run –project BookshopServer/BookshopServer.csproj


For the client-side, we invoke a similar command.

dotnet run –project BookshopClient/BookshopClient.csproj

And in the terminal, we will get the following outputs.


Nice! as you can see it’s not that hard to get everything working ? One thing to note is that we left out the details about TLS and different ways to communicate with the server (i.e. Unary, streaming etc.). I will cover such topics in-depth in the future.

Conclusion

In this article, we looked at how to reuse our Protobuf files to create a client to interact with the server we created in the previous post.

I hope this article series cleared up a lot of confusion that you had about gRPC. Please feel free to share your questions, thoughts, or feedback in the comments section below. Until next time ?

References

https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-6.0&tabs=visual-studio-code

Flatlogic Admin Templates banner

Implementing an API Gateway in ASP.NET Core with Ocelot

This post is about what is an API Gateway and how to build an API Gateway in ASP.NET Core with Ocelot. An API gateway is service that sits between an endpoint and backend APIs, transmitting client requests to an appropriate service of an application. It’s an architectural pattern, which was initially created to support microservices. In this post I am building API Gateway using Ocelot. Ocelot is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system.

Let’s start the implementation.

First we will create two web api applications – both these services returns some hard coded string values. Here is the first web api – CustomersController – which returns list of customers.

using Microsoft.AspNetCore.Mvc;

namespace ServiceA.Controllers;

[ApiController]
[Route(“[controller]”)]
public class CustomersController : ControllerBase
{
private readonly ILogger<CustomersController> _logger;

public CustomersController(ILogger<CustomersController> logger)
{
_logger = logger;
}

[HttpGet(Name = “GetCustomers”)]
public IActionResult Get()
{
return Ok(new[] { “Customer1”, “Customer2”,“Customer3” });
}
}

And here is the second web api – ProductsController.

using Microsoft.AspNetCore.Mvc;

namespace ServiceB.Controllers;

[ApiController]
[Route(“[controller]”)]
public class ProductsController : ControllerBase
{
private readonly ILogger<ProductsController> _logger;

public ProductsController(ILogger<ProductsController> logger)
{
_logger = logger;
}

[HttpGet(Name = “GetProducts”)]
public IActionResult Get()
{
return Ok(new[] { “Product1”, “Product2”,
“Product3”, “Product4”, “Product5” });
}
}

Next we will create the API Gateway. To do this create an ASP.NET Core empty web application using the command – dotnet new web -o ApiGateway. Once we create the gateway application, we need to add the reference of Ocelot nuget package – we can do this using dotnet add package Ocelot. Now we can modify the Program.cs file like this.

using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(“configuration.json”, false, true).AddEnvironmentVariables();

builder.Services.AddOcelot(builder.Configuration);
var app = builder.Build();

app.UseOcelot();
app.Run();

Next you need to configure your API routes using configuration.json. Here is the basic configuration which help to send requests from one endpoint to the web api endpoints.

{
Routes: [
{
DownstreamPathTemplate: /customers,
DownstreamScheme: https,
DownstreamHostAndPorts: [
{
Host: localhost,
Port: 7155
}
],
UpstreamPathTemplate: /api/customers,
UpstreamHttpMethod: [ Get ]
},
{
DownstreamPathTemplate: /products,
DownstreamScheme: https,
DownstreamHostAndPorts: [
{
Host: localhost,
Port: 7295
}
],
UpstreamPathTemplate: /api/products,
UpstreamHttpMethod: [ Get ]
}
],
GlobalConfiguration: {
BaseUrl: https://localhost:7043
}
}

Now run all the three applications and browse the endpoint – https://localhost:7043/api/products – which invokes the ProductsController class GET action method. And if we browse the endpoint – https://localhost:7043/api/customers – which invokes the CustomersController GET action method. In the configuration the UpstreamPathTemplate will be the API Gateway endpoint and API Gateway will transfers the request to the DownstreamPathTemplate endpoint.

Due to some strange reason it was not working properly for me. Today I configured it again and it started working. It is an introductory post. I will blog about some common use cases where API Gateway help and how to deploy it in Azure and all in the future.

Happy Programming 🙂

Flatlogic Admin Templates banner

Introduction to gRPC

Intro

If you have built RESTful or other OpenAPI-like APIs for some time and wondering what’s next for you, then you have come to the right place. This article series discusses leveraging gRPC to build your next API, even multiple services. We will initially look at the main concepts from a high-level view and then move on to the implementation aspects of it.

Motivation

There are many tutorials on getting started there. But the main issue I faced was either, they even made me more confused as there was a lot of contexts lost in the process and brought in way too many third-party libraries or explained a bunch of steps without emphasizing how different pieces work together. Therefore, I thought to create a guide for anyone who’s interested in getting started with gRPC from a hands-on perspective.

This article is the first part of a series on gRPC. The links are down below. If you want to jump ahead, please feel free to do so.

Introduction to gRPC (You are here)
Building a gRPC server with Go
Building a gRPC server with .NET
Building a gRPC client with Go
Building a gRPC client with .NET

Background

One of the popular choices for building APIs nowadays is creating a RESTful service. However, before even coming to REST API, we need to look back to see other forms we used to have in the past.

SOAP – Popular back in the late-90s for building service-oriented architectures (SOA) systems which are known for exchanging bloated XMLs. The benefits were detrimental for distributed applications where the schema was rigid.

REST – Promoted the resource-oriented architecture (ROA)-style distributed applications. Often bulky with JSONs, and everything that the service provides is represented as resources (Eg: /api/v1/users/ or api/v1/books/1234 etc.). Sometimes this could result in too exposing too little data or too much data at the cost of making multiple HTTP calls.

GraphQL – GraphQL takes a step further and exposes a single endpoint that you can use to query or mutate the data through HTTP verbs. It’s still a request-response model and based on the text-based transport protocol, HTTP 1.x.

What if I want to have some bi-directional communication? None of the above solved that. Then we got technologies like WebSockets and Server Sent Eventing.

WebSockets – Built to support bi-directional communication over a single TCP connection. Known to be a very chatty protocol often sending packets back and forth. If you want to know about WebSockets, here is an article that I wrote.

Server Sent Eventing – Another paradigm where the server sends messages to the client once the initial connection has been set up by the client.

There’s a recurring theme going on in the above technologies. It could be issued with messages being (bulky, not strongly typed etc.), inefficient protocols (text-based such as HTTP 1.x etc.).

Hello gRPC

gRPC was born to address some of the challenges we face in the above approaches. In 2015 Google released gRPC to the open-source world. The idea behind gRPC is to have a single client library – Whoever is maintaining gRPC will maintain the client libraries. Because HTTP/2 works on binary format. So the idea is to abstract away the HTTP/2 stuff from you. The developers only have to define their service contracts through requests, responses and RPC calls, and the gRPC framework will handle the rest for us.

? Even before we start learning about gRPC, you must have also thought about what the “g” in GRPC mean? Some say it stands for “Good”; others say it stands for “Google”. You know what? It doesn’t matter as it doesn’t provide more context. You can find all the different variations here, which is, by the way, hilarious! ?


Source: https://grpc.io/docs/what-is-grpc/introduction/

RPC has been around for some time; however, gRPC approaches RPC much cleaner way.

Another term that you’d come across when learning gRPC is inter-process communication or IPCs. GRPC is mainly built to cater to inter-process communication that lets you connect, invoke, and operate, an excellent choice for micro-services like applications. In distributed computing realm, inter-process communication or, in short, IPC refers to passing messages (synchronously or asynchronously) where any application or a node can act as both client and a server.

Does this mean I should replace my current APIs, which are customer-facing? Absolutely not. If you have customer-facing APIs, they should be actually

Protocol Buffers

They are a language-agnostic way to define what your service does. These are commonly known as “IDLs” or Interface Definition Language(s).

So the steps are,

You write the messages. These messages have statically typed fields
You write your services by defining what comes in, what goes out
Compile the proto files and generate the client libraries for your application

It’s also worth mentioning that Protocol Buffers are not the only way to define our IDLs. There are other formats like FlatBuffers, Bond etc.

This is what a protocol buffer looks like:

syntax = “proto3”;

message Book {
string title = 1;
string author = 2;
int32 page_count = 3;
optional string language = 4;
}

message GetBookListRequest {}
message GetBookListResponse { repeated Book books = 1; }

service Inventory {
rpc GetBookList(GetBookListRequest) returns (GetBookListResponse) {}
}

The above is an example that we will use throughout this blog series.


The first line specifies the Protobuf version we will be using. It will be set to proto2 if you don’t specify it.
The Book is a message definition with some statically typed fields such as title, author etc.

GetBookListRequest and GetBookListResponse are also messages composed of the Book type we defined above.
Inventory is a service that says what methods we expose to the remotely invoked clients.

There are many advantages of using Protobufs compared to something like JSON. Once you write the definitions for your service, you can share them with other teams and use them to generate stubs/code that can interact with your service.

Another advantage is that Protobufs are binary encoded. The payload is smaller than JSON, which means it would be efficient to send. This also means that it will use fewer CPU cycles to serialize/deserialize the messages.

gRPC Server & Client

Now that we have the Protobuf definitions for our service, we could generate the Server side and Client side implementations using the Protoc compiler.


We first create the definition of the service with a .proto file
We then generate the server-side code in our preferred language (Go, C#, Java etc.). This code includes the boilerplate code to serialize, deserialize, functions for receiving and responding to messages.
We then generate the client-side code in our preferred language (doesn’t have to be the same language we chose for the server). This includes methods that we can invoke on our server with additional code to serialize, deserialize messages.
Depending on which gRPC mode we choose, the client-server communication happens over an HTTP/2 connection. We will discuss more on these modes in the next section.

gRPC Modes

There are 4 modes of gRPC communication styles. Following are their brief introductions. Feel free to go more in-depth by reading through the official docs.

Unary RPC – More like our traditional APIs where we send a request and receive a single response.
Server Streaming RPC – Client sends a request and reads until the server stops sending messages via a stream.
Client Streaming – Reverse of the above, the client sends messages through the stream and waits for the server to read and return a response.
Bidirectional streaming RPC – Pretty much both (2) & (3) combined – both client and server streams messages both ways.

Pros and Cons

Any technology comes with a set of advantages and disadvantages. Whether you choose to use gRPC might depend on some of these factors.

Pros

Efficient for inter-process communication with all the good stuff that comes with HTTP/2.
Well defined interfaces to be able to communicate also while supporting polyglot development.
Code-generation of client and server stubs with strong types.

Cons

It may not be suitable for external-facing services since most web browsers’ support is limited.
Changing the service definitions might require rework and regeneration of code.
Could be a steeper learning curve compared to other RESTful or GQL like architectural styles.

Conclusion

In this article, we looked at gRPC from a high level. In the next article, we will look at how we can put these into action and generate a gRPC service with Go. Feel free to let me know any feedback or questions. Thanks for reading ✌️

References

https://developers.google.com/protocol-buffers/docs/proto3
https://www.oreilly.com/library/view/grpc-up-and/9781492058328/

https://github.com/grpc-ecosystem/awesome-grpcyou

Flatlogic Admin Templates banner