Équilibrage de charge côté client gRPC
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
L’équilibrage de charge côté client est une fonctionnalité qui permet aux clients gRPC de répartir la charge de manière optimale sur les serveurs disponibles. Cet article explique comment configurer l’équilibrage de charge côté client pour créer des applications gRPC évolutives et à hautes performances dans .NET.
L’équilibrage de charge côté client nécessite :
- .NET 5 ou version ultérieure.
Grpc.Net.Client
version 2.45.0 ou ultérieure.
Configurer l’équilibrage de charge côté client gRPC
L’équilibrage de charge côté client est configuré lors de la création d’un canal. Les deux composants à prendre en compte lors de l’utilisation de l’équilibrage de charge :
- Le programme de résolution, qui résout les adresses du canal. Les programmes de résolution prennent en charge l’obtention d’adresses à partir d’une source externe. Cette fonction est également connue sous le nom de découverte de service.
- L’équilibreur de charge, qui crée des connexions et choisit l’adresse qu’un appel gRPC utilisera.
Les implémentations intégrées de programmes de résolution et d’équilibreurs de charge sont incluses dans Grpc.Net.Client
. L’équilibrage de charge peut également être étendu en écrivant des programmes de résolution et des équilibreurs de charge personnalisés.
Les adresses, les connexions et d’autres états d’équilibrage de charge sont stockés dans une instance GrpcChannel
. Un canal doit être réutilisé lors de l’exécution d’appels gRPC pour que l’équilibrage de charge fonctionne correctement.
Notes
Certaines configurations d’équilibrage de charge utilisent l’injection de dépendances (DI). Les applications qui n’utilisent pas de di peuvent créer un ServiceCollection instance.
Si une application dispose déjà d’une configuration d’ID, comme un site web ASP.NET Core, les types doivent être inscrits auprès de l’instance d’ID existant. GrpcChannelOptions.ServiceProvider
est configuré en obtenant un IServiceProvider à partir d’une DI.
Configurer le résolveur
Le programme de résolution est configuré à l’aide de l’adresse avec laquelle un canal est créé. Le schéma URI de l’adresse spécifie le programme de résolution.
Schéma | Type | Description |
---|---|---|
dns |
DnsResolverFactory |
Résout les adresses en interrogeant le nom d’hôte pour les enregistrements d’adresses DNS. |
static |
StaticResolverFactory |
Résout les adresses spécifiées par l’application. Recommandé si une application connaît déjà les adresses qu’elle appelle. |
Un canal n’appelle pas directement un URI qui correspond à un programme de résolution. Au lieu de cela, un programme de résolution correspondant est créé et utilisé pour résoudre les adresses.
Par exemple, en utilisant GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure })
:
- Le schéma
dns
est mappé àDnsResolverFactory
. Une nouvelle instance d’un programme de résolution DNS est créée pour le canal. - Le programme de résolution effectue une requête DNS pour
my-example-host
et obtient deux résultats :127.0.0.100
et127.0.0.101
. - L’équilibreur de charge utilise
127.0.0.100:80
et127.0.0.101:80
pour créer des connexions et effectuer des appels gRPC.
DnsResolverFactory
Le DnsResolverFactory
crée un programme de résolution conçu pour obtenir des adresses à partir d’une source externe. La résolution DNS est couramment utilisée pour équilibrer la charge sur les instances de pod qui ont des services sans affichage Kubernetes.
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Le code précédent :
- Configure le canal créé avec l’adresse
dns:///my-example-host
.- Le schéma
dns
est mappé àDnsResolverFactory
. my-example-host
est le nom d’hôte à résoudre.- Aucun port n’étant spécifié dans l’adresse, les appels gRPC sont envoyés au port 80. Il s’agit du port par défaut pour les canaux non sécurisés. Un port peut éventuellement être spécifié après le nom d’hôte. Par exemple,
dns:///my-example-host:8080
configure les appels gRPC à envoyer au port 8080.
- Le schéma
- Ne spécifie pas d’équilibreur de charge. Par défaut, le canal est un équilibreur de charge de premier choix.
- Démarre l’appel gRPC
SayHello
:- Le programme de résolution DNS obtient des adresses pour le nom d’hôte
my-example-host
. - L’équilibreur de charge de premier choix tente de se connecter à l’une des adresses résolues.
- L’appel est envoyé à la première adresse à laquelle le canal se connecte correctement.
- Le programme de résolution DNS obtient des adresses pour le nom d’hôte
Mise en cache d’adresses DNS
Les performances sont importantes lors de l’équilibrage de charge. La latence de résolution des adresses est éliminée des appels gRPC par la mise en cache des adresses. Un programme de résolution est appelé lors du premier appel gRPC, et les appels suivants utilisent le cache.
Les adresses sont automatiquement actualisées si une connexion est interrompue. L’actualisation est importante dans les scénarios où les adresses changent au moment du runtime. Par exemple, dans Kubernetes, un pod redémarré déclenche le programme de résolution DNS pour l’actualisation et l’obtention de la nouvelle adresse du pod.
Par défaut, un programme de résolution DNS est actualisé si une connexion est interrompue. Le programme de résolution DNS peut également s’actualiser sur un intervalle périodique. Cela peut être utile pour détecter rapidement de nouvelles instances de pod.
services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
Le code précédent crée un DnsResolverFactory
avec un intervalle d’actualisation et l’enregistre avec l’injection de dépendances. Pour plus d’informations sur l’utilisation d’un programme de résolution configuré sur mesure, consultez Configurer des programmes de résolution et des équilibreurs de charge personnalisés.
StaticResolverFactory
Un programme de résolution statique est fourni par StaticResolverFactory
. Ce programme de résolution :
- N’appelle pas une source externe. Au lieu de cela, l’application cliente configure les adresses.
- Est conçu pour les situations dans lesquelles une application connaît déjà les adresses qu’elle appelle.
var factory = new StaticResolverFactory(addr => new[]
{
new BalancerAddress("localhost", 80),
new BalancerAddress("localhost", 81)
});
var services = new ServiceCollection();
services.AddSingleton<ResolverFactory>(factory);
var channel = GrpcChannel.ForAddress(
"static:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
Le code précédent :
- Crée un
StaticResolverFactory
. Cette fabrique connaît deux adresses :localhost:80
etlocalhost:81
. - Enregistre la fabrique avec l’injection de dépendances (DI).
- Configure le canal créé avec :
- L’adresse
static:///my-example-host
. Le schémastatic
est mappé à un programme de résolution statique. - Définit
GrpcChannelOptions.ServiceProvider
avec le fournisseur de services DI.
- L’adresse
Cet exemple crée un nouveau ServiceCollection pour la DI. Supposons qu’une application dispose déjà d’une configuration de DI, comme un site web ASP.NET Core. Dans ce cas, les types doivent être inscrits auprès de l’instance DI existante. GrpcChannelOptions.ServiceProvider
est configuré en obtenant un IServiceProvider à partir d’une DI.
Configurer l'équilibreur de charge
Un équilibreur de charge est spécifié dans un service config
à l’aide de la collection ServiceConfig.LoadBalancingConfigs
. Deux équilibreurs de charge sont intégrés et mappés aux noms de configuration de l’équilibreur de charge :
Nom | Type | Description |
---|---|---|
pick_first |
PickFirstLoadBalancerFactory |
Tente de se connecter aux adresses jusqu’à ce qu’une connexion soit établie avec succès. Les appels gRPC sont tous effectués à la première connexion réussie. |
round_robin |
RoundRobinLoadBalancerFactory |
Tente de se connecter à toutes les adresses. Les appels gRPC sont distribués entre toutes les connexions réussies à l’aide d’une logique round-robin. |
service config
est une abréviation de la configuration du service et est représenté par le type ServiceConfig
. Il existe plusieurs façons pour un canal d’obtenir un service config
avec un équilibreur de charge configuré :
- Une application peut spécifier un
service config
lors de la création d’un canal à l’aide deGrpcChannelOptions.ServiceConfig
. - Un programme de résolution peut également résoudre un
service config
pour un canal. Cette fonctionnalité permet à une source externe de spécifier la façon dont ses appelants doivent effectuer l’équilibrage de charge. Le fait qu’un programme de résolution prenne en charge la résolution d’unservice config
dépend de l’implémentation du programme de résolution. Désactivez cette fonctionnalité avecGrpcChannelOptions.DisableResolverServiceConfig
. - Si aucun
service config
n’est fourni ou si leservice config
n’a pas d’équilibreur de charge configuré, le canal est défini par défaut surPickFirstLoadBalancerFactory
.
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new RoundRobinConfig() } }
});
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Le code précédent :
- Spécifie un
RoundRobinLoadBalancerFactory
dans leservice config
. - Démarre l’appel gRPC
SayHello
:DnsResolverFactory
crée un programme de résolution qui obtient des adresses pour le nom d’hôtemy-example-host
.- L’équilibreur de charge round-robin tente de se connecter à toutes les adresses résolues.
- Les appels gRPC sont distribués uniformément à l’aide de la logique round-robin.
Configurer les informations d’identification du canal
Un canal doit savoir si les appels gRPC sont envoyés à l’aide de la sécurité du transport. http
et https
ne font plus partie de l’adresse, le schéma spécifie désormais un programme de résolution. Credentials
doit donc être configuré sur les options de canal lors de l’utilisation de l’équilibrage de charge.
ChannelCredentials.SecureSsl
: les appels gRPC sont sécurisés avec le protocole TLS. Équivalent à une adressehttps
.ChannelCredentials.Insecure
: les appels gRPC n’utilisent pas la sécurité de transport. Équivalent à une adressehttp
.
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Utiliser l’équilibrage de charge avec la fabrique de client gRPC
La fabrique de client gRPC peut être configurée pour utiliser l’équilibrage de charge :
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("dns:///my-example-host");
})
.ConfigureChannel(o => o.Credentials = ChannelCredentials.Insecure);
builder.Services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
var app = builder.Build();
Le code précédent :
- Configure le client avec une adresse d’équilibrage de charge.
- Spécifie les informations d’identification du canal.
- Inscrit les types d’ID auprès de l’application IServiceCollection.
Écrire des programmes de résolution personnalisés et des équilibreurs de charge
L’équilibrage de charge côté client est extensible :
- Implémentez
Resolver
pour créer un programme de résolution personnalisé et résoudre les adresses à partir d’une nouvelle source de données. - Implémentez
LoadBalancer
pour créer un équilibreur de charge personnalisé avec un nouveau comportement d’équilibrage de charge.
Important
Les API utilisées pour étendre l’équilibrage de charge côté client sont expérimentales. Elles peuvent changer sans préavis.
Créer un programme de résolution personnalisé
Un programme de résolution :
- Implémente
Resolver
et est créé par unResolverFactory
. Créez un programme de résolution personnalisé en implémentant ces types. - Est responsable de la résolution des adresses qu’un équilibreur de charge utilise.
- Peut éventuellement fournir une configuration de service.
public class FileResolver : PollingResolver
{
private readonly Uri _address;
private readonly int _port;
public FileResolver(Uri address, int defaultPort, ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_address = address;
_port = defaultPort;
}
public override async Task ResolveAsync(CancellationToken cancellationToken)
{
// Load JSON from a file on disk and deserialize into endpoints.
var jsonString = await File.ReadAllTextAsync(_address.LocalPath);
var results = JsonSerializer.Deserialize<string[]>(jsonString);
var addresses = results.Select(r => new BalancerAddress(r, _port)).ToArray();
// Pass the results back to the channel.
Listener(ResolverResult.ForResult(addresses));
}
}
public class FileResolverFactory : ResolverFactory
{
// Create a FileResolver when the URI has a 'file' scheme.
public override string Name => "file";
public override Resolver Create(ResolverOptions options)
{
return new FileResolver(options.Address, options.DefaultPort, options.LoggerFactory);
}
}
Dans le code précédent :
- L'objet
FileResolverFactory
implémente l'objetResolverFactory
. Il mappe au schémafile
et crée des instancesFileResolver
. - L'objet
FileResolver
implémente l'objetPollingResolver
.PollingResolver
est un type de base abstrait qui facilite l’implémentation d’un programme de résolution avec une logique asynchrone en remplaçantResolveAsync
. - Dans :
ResolveAsync
- L’URI de fichier est converti en chemin d’accès local. Par exemple,
file:///c:/addresses.json
devientc:\addresses.json
. - JSON est chargé à partir du disque et converti en une collection d’adresses.
- L’écouteur est appelé avec des résultats pour informer le canal que les adresses sont disponibles.
- L’URI de fichier est converti en chemin d’accès local. Par exemple,
Créer un équilibreur de charge personnalisé
Un équilibreur de charge :
- Implémente
LoadBalancer
et est créé par unLoadBalancerFactory
. Créez un équilibreur de charge et une fabrique personnalisés en implémentant ces types. - Reçoit des adresses à partir d’un programme de résolution et crée des instances
Subchannel
. - Suit l’état de la connexion et crée un
SubchannelPicker
. Le canal utilise le sélecteur en interne pour sélectionner des adresses lors des appels gRPC.
Le SubchannelsLoadBalancer
est :
- Une classe de base abstraite qui implémente
LoadBalancer
. - Gère la création d’instances
Subchannel
à partir d’adresses. - Facilite l’implémentation d’une stratégie de sélection personnalisée sur une collection de sous-canaux.
public class RandomBalancer : SubchannelsLoadBalancer
{
public RandomBalancer(IChannelControlHelper controller, ILoggerFactory loggerFactory)
: base(controller, loggerFactory)
{
}
protected override SubchannelPicker CreatePicker(List<Subchannel> readySubchannels)
{
return new RandomPicker(readySubchannels);
}
private class RandomPicker : SubchannelPicker
{
private readonly List<Subchannel> _subchannels;
public RandomPicker(List<Subchannel> subchannels)
{
_subchannels = subchannels;
}
public override PickResult Pick(PickContext context)
{
// Pick a random subchannel.
return PickResult.ForSubchannel(_subchannels[Random.Shared.Next(0, _subchannels.Count)]);
}
}
}
public class RandomBalancerFactory : LoadBalancerFactory
{
// Create a RandomBalancer when the name is 'random'.
public override string Name => "random";
public override LoadBalancer Create(LoadBalancerOptions options)
{
return new RandomBalancer(options.Controller, options.LoggerFactory);
}
}
Dans le code précédent :
- L'objet
RandomBalancerFactory
implémente l'objetLoadBalancerFactory
. Il mappe au nom de la stratégierandom
et crée des instancesRandomBalancer
. - L'objet
RandomBalancer
implémente l'objetSubchannelsLoadBalancer
. Il crée unRandomPicker
qui sélectionne aléatoirement un sous-canal.
Configurer des programmes de résolution personnalisés et des équilibreurs de charge
Les programmes de résolution personnalisés et les équilibreurs de charge doivent être enregistrés avec l’injection de dépendances (DI) lorsqu’ils sont utilisés. Deux cas de figure peuvent se présenter :
- Si une application utilise déjà la DI, par exemple une application web ASP.NET Core, elle peut être enregistrée avec la configuration de DI existante. Un IServiceProvider peut être résolu à partir de la DI et passé au canal à l’aide de
GrpcChannelOptions.ServiceProvider
. - Si une application n’utilise pas la DI, créez :
- Un ServiceCollection avec les types enregistrés avec lui.
- Un fournisseur de services utilisant BuildServiceProvider.
var services = new ServiceCollection();
services.AddSingleton<ResolverFactory, FileResolverFactory>();
services.AddSingleton<LoadBalancerFactory, RandomLoadBalancerFactory>();
var channel = GrpcChannel.ForAddress(
"file:///c:/data/addresses.json",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new LoadBalancingConfig("random") } },
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
Le code précédent :
- Crée un
ServiceCollection
et enregistre de nouvelles implémentations de programme de résolution et d’équilibreur de charge. - Crée un canal configuré pour utiliser les nouvelles implémentations :
ServiceCollection
est intégré à unIServiceProvider
et défini surGrpcChannelOptions.ServiceProvider
.- L’adresse du canal est
file:///c:/data/addresses.json
. Le schémafile
est mappé àFileResolverFactory
. - Le nom de l'équilibreur de charge
service config
estrandom
. Mappe àRandomLoadBalancerFactory
.
Pourquoi l’équilibrage de charge est important
HTTP/2 multiplexe plusieurs appels sur une seule connexion TCP. Si gRPC et HTTP/2 sont utilisés avec un équilibreur de la charge réseau (NLB), la connexion est transférée à un serveur et tous les appels gRPC sont envoyés à ce serveur. Les autres instances de serveur sur la NLB sont inactives.
Les équilibreurs de la charge réseau sont une solution courante pour l’équilibrage de charge, car ils sont rapides et légers. Par exemple, Kubernetes utilise par défaut un équilibreur de la charge réseau pour équilibrer les connexions entre les instances de pod. Toutefois, les équilibreurs de la charge réseau ne sont pas efficaces pour distribuer la charge lorsqu’ils sont utilisés avec gRPC et HTTP/2.
Équilibrage de charge proxy ou côté client ?
gRPC et HTTP/2 peuvent être équilibrés efficacement à l’aide d’un proxy d’équilibreur de charge d’application ou d’un équilibrage de la charge côté client. Ces deux options permettent de distribuer des appels gRPC individuels sur les serveurs disponibles. Choisir entre le proxy et l’équilibrage de la charge côté client est un choix architectural. Il y a des avantages et des inconvénients pour chacun.
Proxy : les appels gRPC sont envoyés au proxy, le proxy prend une décision d’équilibrage de charge et l’appel gRPC est envoyé au point de terminaison final. Le proxy est chargé de connaître les points de terminaison. L’utilisation d’un proxy ajoute :
- Un tronçon réseau supplémentaire vers les appels gRPC.
- De la latence et consomme davantage de ressources.
- Le serveur proxy doit être configuré correctement.
Équilibrage de la charge côté client : le client gRPC prend une décision d’équilibrage de charge lorsqu’un appel gRPC est démarré. L’appel gRPC est envoyé directement au point de terminaison final. Lors de l’utilisation de l’équilibrage de la charge côté client :
- Le client est chargé de connaître les points de terminaison disponibles et de prendre des décisions d’équilibrage de charge.
- Une configuration client supplémentaire est requise.
- Les appels gRPC hautes performances et à charge équilibrée éliminent le besoin d’un proxy.