Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 10-versie van dit artikel voor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie het .NET- en .NET Core-ondersteuningsbeleid voor meer informatie. Zie de .NET 10-versie van dit artikel voor de huidige release.
Door James Newton-King
Taakverdeling aan de clientzijde is een functie waarmee gRPC-clients de belasting optimaal kunnen verdelen over beschikbare servers. In dit artikel wordt beschreven hoe u taakverdeling aan de clientzijde configureert om schaalbare, krachtige gRPC-apps te maken in .NET.
Taakverdeling aan de clientzijde vereist:
- .NET 5 of hoger.
-
Grpc.Net.Clientversie 2.45.0 of hoger.
GRPC-taakverdeling aan clientzijde configureren
Taakverdeling aan de clientzijde wordt geconfigureerd wanneer een kanaal wordt gemaakt. De twee onderdelen waarmee u rekening moet houden bij het gebruik van taakverdeling:
- De resolver, die de adressen voor het kanaal oplost. Resolvers ondersteunen het ophalen van adressen van een externe bron. Dit wordt ook wel het ontdekken van services genoemd.
- De load balancer, die verbindingen maakt en het adres kiest dat door een gRPC-aanroep wordt gebruikt.
Ingebouwde implementaties van resolvers en load balancers zijn opgenomen in Grpc.Net.Client. Taakverdeling kan ook worden uitgebreid door aangepaste resolvers en load balancers te schrijven.
Adressen, verbindingen en andere taakverdelingsstatus worden opgeslagen in een GrpcChannel exemplaar. Een kanaal moet opnieuw worden gebruikt bij het maken van gRPC-aanroepen om taakverdeling correct te laten werken.
Opmerking
Sommige taakverdelingsconfiguratie maakt gebruik van afhankelijkheidsinjectie (DI). Apps die DI niet gebruiken, kunnen een ServiceCollection exemplaar maken.
Als een app al een DI-installatie heeft, zoals een ASP.NET Core-website, moeten typen worden geregistreerd bij het bestaande DI-exemplaar.
GrpcChannelOptions.ServiceProvider is geconfigureerd door een IServiceProvider van de DI op te halen.
Resolver configureren
De resolver wordt geconfigureerd met het adres waarmee een kanaal wordt gemaakt. Het URI-schema van het adres specificeert de resolver.
| Plan | Typologie | Description |
|---|---|---|
dns |
DnsResolverFactory |
Hiermee worden adressen omgezet door een query uit te voeren op de hostnaam voor DNS-adresrecords. |
static |
StaticResolverFactory |
Hiermee worden adressen opgelost die door de app zijn opgegeven. Aanbevolen als een app al de adressen kent die worden aanroepen. |
Een kanaal roept niet rechtstreeks een URI aan die overeenkomt met een resolver. In plaats daarvan wordt een overeenkomende resolver gemaakt en gebruikt om de adressen op te lossen.
Gebruik bijvoorbeeld GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure }):
- Het
dnsschema komt overeen metDnsResolverFactory. Er wordt een nieuw exemplaar van een DNS-resolver gemaakt voor het kanaal. - De resolver maakt een DNS-query voor
my-example-hosten haalt twee resultaten op:127.0.0.100en127.0.0.101. - De load balancer maakt gebruik van
127.0.0.100:80en127.0.0.101:80om verbindingen te creëren en gRPC-aanroepen te doen.
DnsResolverFactory
Een resolver wordt gecreëerd die is ontworpen om adressen uit een externe bron op te halen. DNS-omzetting wordt vaak gebruikt om belasting te verdelen over pod-exemplaren met een Kubernetes headless services.
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" });
De voorgaande code:
- Hiermee configureert u het gemaakte kanaal met het adres
dns:///my-example-host.- Het
dnsschema komt overeen metDnsResolverFactory. -
my-example-hostis de hostnaam die moet worden opgelost. - Er is geen poort opgegeven in het adres, dus gRPC-aanroepen worden verzonden naar poort 80. Dit is de standaardpoort voor onbeveiligde kanalen. Een poort kan eventueel worden opgegeven na de hostnaam. Configureert bijvoorbeeld
dns:///my-example-host:8080gRPC-aanroepen die moeten worden verzonden naar poort 8080.
- Het
- Geeft geen load balancer op. Het kanaal wordt standaard ingesteld op een pick first load balancer.
- Start de gRPC-aanroep
SayHello:- DNS-resolver haalt adressen op voor de hostnaam
my-example-host. - Kies de eerste load balancer die probeert verbinding te maken met een van de opgeloste adressen.
- De aanroep wordt verzonden naar het eerste adres waarnaar het kanaal verbinding heeft gemaakt.
- DNS-resolver haalt adressen op voor de hostnaam
DNS-adres-caching
Prestaties zijn belangrijk bij taakverdeling. De latentie van het oplossen van adressen wordt verwijderd uit gRPC-aanroepen door de adressen in de cache te plaatsen. Een resolver wordt aangeroepen bij het maken van de eerste gRPC-aanroep en volgende aanroepen maken gebruik van de cache.
Adressen worden automatisch vernieuwd als een verbinding wordt onderbroken. Vernieuwen is belangrijk in scenario's waarin adressen tijdens runtime veranderen. In Kubernetes activeert een opnieuw gestarte pod bijvoorbeeld de DNS-resolver om te vernieuwen en het nieuwe adres van de pod op te halen.
Standaard wordt een DNS-resolver vernieuwd als een verbinding wordt onderbroken. De DNS-resolver kan zichzelf ook optioneel vernieuwen met een periodiek interval. Dit kan handig zijn voor het snel detecteren van nieuwe pod-exemplaren.
services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
De voorgaande code maakt een DnsResolverFactory aan met een vernieuwingsinterval en registreert deze met behulp van afhankelijkheidsinjectie. Zie Aangepaste resolvers en load balancers configureren voor meer informatie over het gebruik van een op maat geconfigureerde resolver.
StaticResolverFactory
Een statische resolver wordt geleverd door StaticResolverFactory. Deze resolver:
- Hiermee wordt geen externe bron aangeroepen. In plaats daarvan configureert de client-app de adressen.
- Is ontworpen voor situaties waarin een app al weet wat de adressen zijn die de app aanroept.
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);
De voorgaande code:
- Maak een
StaticResolverFactory. Deze fabriek kent twee adressen:localhost:80enlocalhost:81. - Registreert de fabriek met afhankelijkheidsinjectie (DI).
- Hiermee configureert u het gemaakte kanaal met:
- Het adres
static:///my-example-host. Hetstaticschema wordt toegewezen aan een statische resolver. - Stel
GrpcChannelOptions.ServiceProviderin met de DI-serviceprovider.
- Het adres
In dit voorbeeld wordt een nieuwe ServiceCollection voor DI gemaakt. Stel dat een app al EEN DI-installatie heeft, zoals een ASP.NET Core-website. In dat geval moeten typen worden geregistreerd bij het bestaande DI-exemplaar.
GrpcChannelOptions.ServiceProvider is geconfigureerd door een IServiceProvider van de DI op te halen.
Configureer de load balancer
Een load balancer wordt opgegeven in een service config met behulp van de ServiceConfig.LoadBalancingConfigs verzameling. Twee load balancers zijn ingebouwd en corresponderen met de configuratienamen van load balancers.
| Naam | Typologie | Description |
|---|---|---|
pick_first |
PickFirstLoadBalancerFactory |
Probeert verbinding te maken met adressen totdat een verbinding tot stand is gebracht. gRPC-aanroepen worden allemaal uitgevoerd bij de eerste geslaagde verbinding. |
round_robin |
RoundRobinLoadBalancerFactory |
Pogingen om verbinding te maken met alle adressen. gRPC-aanroepen worden verdeeld over alle geslaagde verbindingen met behulp van round robin-logica . |
service config is een afkorting van de serviceconfiguratie en wordt vertegenwoordigd door het ServiceConfig type. Er zijn verschillende manieren waarop een kanaal een service config kan krijgen met een geconfigureerde load balancer.
- Een app kan een
service configspecificeren wanneer een kanaal wordt gemaakt met behulp vanGrpcChannelOptions.ServiceConfig. - Een resolver kan ook een
service configvoor een kanaal oplossen. Met deze functie kan een externe bron opgeven hoe de aanroepers taakverdeling moeten uitvoeren. Of een resolver ondersteuning biedt voor het oplossen van een oplossingservice config, is afhankelijk van de implementatie van de resolver. Schakel deze functie uit metGrpcChannelOptions.DisableResolverServiceConfig. - Als er geen
service configis opgegeven of als erservice configgeen load balancer is geconfigureerd, wordt het kanaal standaardPickFirstLoadBalancerFactoryingesteld op .
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" });
De voorgaande code:
- Hiermee specificeert u een
RoundRobinLoadBalancerFactoryin deservice config. - Start de gRPC-aanroep
SayHello:-
DnsResolverFactorymaakt een resolver die adressen voor de hostnaammy-example-hostophaalt. - De Round-robin load balancer probeert verbinding te maken met alle de opgeloste adressen.
- gRPC-aanroepen worden gelijkmatig gedistribueerd met behulp van round robin-logica.
-
Kanaalreferenties configureren
Een kanaal moet weten of gRPC-aanroepen worden verzonden met transportbeveiliging.
http en https maakt geen deel meer uit van het adres, het schema geeft nu een resolver op, dus Credentials moet worden geconfigureerd voor kanaalopties bij het gebruik van taakverdeling.
-
ChannelCredentials.SecureSsl- gRPC-aanroepen worden beveiligd met Tls (Transport Layer Security). Gelijk aan eenhttpsadres. -
ChannelCredentials.Insecure- gRPC-aanroepen maken geen gebruik van transportbeveiliging. Gelijk aan eenhttpadres.
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" });
Taakverdeling gebruiken met gRPC-clientfactory
gRPC-clientfactory kan worden geconfigureerd voor het gebruik van taakverdeling:
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();
De voorgaande code:
- Hiermee configureert u de client met een adres voor load-balancing.
- Hiermee specificeert u kanaalidentificatiegegevens.
- Registreert DI-typen bij de app IServiceCollection.
Aangepaste resolvers en load balancers schrijven
Taakverdeling aan de clientzijde is uitbreidbaar:
- Implementeer
Resolverom een aangepaste resolver te maken en adressen op te lossen vanuit een nieuwe gegevensbron. - Implementeer
LoadBalancerom een aangepaste load balancer te maken met nieuw taakverdelingsgedrag.
Belangrijk
De API's die worden gebruikt om taakverdeling aan de clientzijde uit te breiden, zijn experimenteel. Ze kunnen zonder kennisgeving veranderen.
Een aangepaste resolver maken
Een resolver:
-
Resolverwordt geïmplementeerd en gemaakt door eenResolverFactory. Maak een aangepaste resolver door deze typen te implementeren. - Is verantwoordelijk voor het oplossen van de adressen die een load balancer gebruikt.
- Kan eventueel een serviceconfiguratie bieden.
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);
}
}
In de voorgaande code:
-
FileResolverFactoryimplementeertResolverFactory. Het wordt toegewezen aan hetfileschema en maaktFileResolverinstantiaties. -
FileResolverimplementeertPollingResolver.PollingResolveris een abstract basistype waarmee u eenvoudig een resolver met asynchrone logica kunt implementeren door deze te overschrijvenResolveAsync. - In
ResolveAsync:- De bestands-URI wordt geconverteerd naar een lokaal pad. Zo wordt
file:///c:/addresses.jsongewijzigd inc:\addresses.json. - JSON wordt vanaf schijf geladen en geconverteerd naar een verzameling adressen.
- Listener wordt aangeroepen met resultaten om het kanaal te laten weten dat er adressen beschikbaar zijn.
- De bestands-URI wordt geconverteerd naar een lokaal pad. Zo wordt
Een aangepaste load balancer maken
Een load balancer:
-
LoadBalancerwordt geïmplementeerd en gemaakt door eenLoadBalancerFactory. Maak een aangepaste load balancer en factory door deze typen te implementeren. - Krijgt adressen van een resolver en creëert
Subchannelinstanties. - Volgt de toestand van de verbinding en creëert een
SubchannelPicker. Het kanaal gebruikt intern de kiezer om adressen te kiezen bij het maken van gRPC-aanroepen.
Dit is de SubchannelsLoadBalancer:
- Een abstracte basisklasse die implementeert
LoadBalancer. - Beheert het genereren van instanties
Subchanneluit adressen. - Hiermee kunt u eenvoudig een aangepast pick-beleid implementeren voor een verzameling subkanalen.
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);
}
}
In de voorgaande code:
-
RandomBalancerFactoryimplementeertLoadBalancerFactory. Dit komt overeen met derandombeleidsnaam en genereertRandomBalancerexemplaren. -
RandomBalancerimplementeertSubchannelsLoadBalancer. Er wordt eenRandomPickergemaakt die willekeurig een subkanaal kiest.
Configureer aangepaste resolvers en load balancers
Aangepaste resolvers en load balancers moeten worden geregistreerd via dependency injection (DI) wanneer ze worden gebruikt. Er zijn een aantal opties:
- Als een app al gebruikmaakt van DI, zoals een ASP.NET Core-web-app, kunnen deze worden geregistreerd bij de bestaande DI-configuratie. Een IServiceProvider kan worden verkregen uit DI en gegeven aan het kanaal met behulp van
GrpcChannelOptions.ServiceProvider. - Als een app geen di gebruikt, maakt u het volgende:
- Een ServiceCollection met typen die ermee zijn geregistreerd.
- Een serviceprovider die gebruikmaakt van 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);
De voorgaande code:
- Hiermee maakt u een
ServiceCollectionimplementatie en registreert u nieuwe resolver- en load balancer-implementaties. - Hiermee maakt u een kanaal dat is geconfigureerd voor het gebruik van de nieuwe implementaties:
-
ServiceCollectionis ingebouwd in eenIServiceProvideren ingesteld opGrpcChannelOptions.ServiceProvider. - Kanaaladres is
file:///c:/data/addresses.json. Hetfileschema komt overeen metFileResolverFactory. -
service configde naam van de load balancer israndom. Wordt toegewezen aanRandomLoadBalancerFactory.
-
Waarom taakverdeling belangrijk is
HTTP/2 multiplexen meerdere verzoeken op één TCP-verbinding. Als gRPC en HTTP/2 worden gebruikt met een netwerk load balancer (NLB), wordt de verbinding doorgestuurd naar een server en worden alle gRPC-aanroepen naar die ene server verzonden. De andere serverinstanties op de NLB zijn inactief.
Netwerktaakverdelers zijn een algemene oplossing voor taakverdeling, omdat ze snel en lichtgewicht zijn. Kubernetes gebruikt bijvoorbeeld standaard een netwerk load balancer om verbindingen tussen pod-exemplaren te verdelen. Netwerktaakverdelers zijn echter niet effectief bij het distribueren van belasting wanneer ze worden gebruikt met gRPC en HTTP/2.
Taakverdeling aan de clientzijde of proxy?
gRPC en HTTP/2 kunnen effectief worden verdeeld met behulp van een load balancer-proxy voor toepassingen of taakverdeling aan de clientzijde. Met beide opties kunnen afzonderlijke gRPC-aanroepen worden gedistribueerd over beschikbare servers. Kiezen tussen proxy- en clienttaakverdeling is een architectuurkeuze. Er zijn voor- en nadelen voor elk.
Proxy: gRPC-aanroepen worden verzonden naar de proxy, de proxy maakt een taakverdelingsbeslissing en de gRPC-aanroep wordt verzonden naar het uiteindelijke eindpunt. De proxy is verantwoordelijk voor het weten over eindpunten. Met behulp van een proxy voegt u het volgende toe:
- Een extra netwerkovergang bij gRPC-oproepen.
- Latentie en verbruikt extra middelen.
- Proxyserver moet correct zijn ingesteld en geconfigureerd.
Taakverdeling aan de clientzijde: De gRPC-client maakt een taakverdelingsbeslissing wanneer een gRPC-aanroep wordt gestart. De gRPC-aanroep wordt rechtstreeks naar het uiteindelijke eindpunt verzonden. Wanneer u taakverdeling aan de clientzijde gebruikt:
- De client is verantwoordelijk voor het weten over beschikbare eindpunten en het nemen van beslissingen over taakverdeling.
- Aanvullende clientconfiguratie is vereist.
- Hoge prestaties, gRPC-aanroepen met gelijke taakverdeling elimineren de noodzaak van een proxy.