...

/

Scaling gRPC Applications via Custom Load Balancing

Scaling gRPC Applications via Custom Load Balancing

Learn how to scale a gRPC application via a custom load balancer.

By relying on a DNS load balancer or something similar, the .NET implementation of gRPC clients allows us to add our custom load balancer with any arbitrary logic. We can apply this to the resolver that pulls the list of the endpoints to connect to. However, we can also create classes with our load balancing logic.

In this lesson, we'll look at how this is done by applying the changes to the following setup, which contains both the gRPC client and server.

{
  "profiles": {
    "BasicGrpcService": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": false,
      "applicationUrl": "http://127.0.0.1:5100;https://127.0.0.1:7100",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}
Baseline solution with a gRPC client and a gRPC server

We'll create a custom load balancer and a custom address resolver. The load balancer will select a random address from the list of available addresses. The resolver will read addresses from a CSV file.

Creating a custom load balancer

To add a custom load balancer, we'll add a file with the name of RandomizedBalancer.cs to the WebApiGrpcClient project folder. We'll populate the file with the following content:

Press + to interact
using Grpc.Net.Client.Balancer;
namespace WebApiGrpcClient;
public class RandomizedBalancer : SubchannelsLoadBalancer
{
public RandomizedBalancer(IChannelControlHelper controller, ILoggerFactory loggerFactory)
: base(controller, loggerFactory)
{
}
protected override SubchannelPicker CreatePicker(IReadOnlyList<Subchannel> readySubchannels)
{
return new RandomizedPicker(readySubchannels);
}
private class RandomizedPicker : SubchannelPicker
{
private readonly IReadOnlyList<Subchannel> _subchannels;
public RandomizedPicker(IReadOnlyList<Subchannel> subchannels)
{
_subchannels = subchannels;
}
public override PickResult Pick(PickContext context)
{
return PickResult.ForSubchannel(_subchannels[Random.Shared.Next(0, _subchannels.Count)]);
}
}
public class RandomizedBalancerFactory : LoadBalancerFactory
{
public override string Name => "randomized";
public override LoadBalancer Create(LoadBalancerOptions options)
{
return new RandmomizedBalancer(options.Controller, options.LoggerFactory);
}
}
}

So, to create a custom load balancer, we'll need to inherit from the SubchannelsLoadBalancer base class from the Grpc.Net.Client.Balancer namespace, as we do on line 5. We also need to create a picker class responsible for selecting the addresses and will be returned by the CreatePicker override method of the custom load balancer class. The picker class needs to inherit from the SubchannelPicker class. We ...