Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Il bilanciamento del carico lato client è una funzionalità che consente ai client gRPC di distribuire il carico in modo ottimale tra i server disponibili. Questo articolo illustra come configurare il bilanciamento del carico lato client per creare app gRPC a prestazioni elevate e scalabili in .NET.
Il bilanciamento del carico lato client richiede:
- .NET 5 o versione successiva.
-
Grpc.Net.Clientversione 2.45.0 o successiva.
Configurare il bilanciamento del carico lato client gRPC
Il bilanciamento del carico lato client viene configurato quando viene creato un canale. I due componenti da considerare quando si usa il bilanciamento del carico:
- Resolver, che risolve gli indirizzi per il canale. I resolver supportano il recupero di indirizzi da un'origine esterna. Questa operazione è nota anche come individuazione dei servizi.
- Il servizio di bilanciamento del carico, che crea connessioni e seleziona l'indirizzo che verrà usato da una chiamata gRPC.
Le implementazioni predefinite di resolver e servizi di bilanciamento del carico sono incluse in Grpc.Net.Client. Il bilanciamento del carico può essere esteso anche scrivendo resolver personalizzati e servizi di bilanciamento del carico.
Gli indirizzi, le connessioni e altri stati di bilanciamento del carico vengono archiviati in un'istanza GrpcChannel di . Un canale deve essere riutilizzato quando si effettuano chiamate gRPC per il corretto funzionamento del bilanciamento del carico.
Nota
Alcune configurazioni di bilanciamento del carico usano l'inserimento delle dipendenze. Le app che non usano l'inserimento delle dipendenze possono creare un'istanza ServiceCollection di .
Se un'app ha già la configurazione dell'inserimento delle dipendenze, ad esempio un sito Web di ASP.NET Core, i tipi devono essere registrati con l'istanza di inserimento delle dipendenze esistente.
GrpcChannelOptions.ServiceProvider viene configurato recuperando un'istanza dall'inserimento delle dipendenze IServiceProvider .
Configurare il sistema di risoluzione
Il sistema di risoluzione viene configurato usando l'indirizzo con cui viene creato un canale. Lo schema URI dell'indirizzo specifica il resolver.
| Schema | Tipo | Descrizione |
|---|---|---|
dns |
DnsResolverFactory |
Risolve gli indirizzi eseguendo una query sul nome host per i record di indirizzi DNS. |
static |
StaticResolverFactory |
Risolve gli indirizzi specificati dall'app. Consigliato se un'app conosce già gli indirizzi che chiama. |
Un canale non chiama direttamente un URI che corrisponde a un resolver. Viene invece creato e usato un sistema di risoluzione corrispondente per risolvere gli indirizzi.
Ad esempio, usando GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure }):
- Lo schema esegue il
dnsmapping aDnsResolverFactory. Viene creata una nuova istanza di un resolver DNS per il canale. - Il resolver esegue una query DNS per
my-example-hoste ottiene due risultati:127.0.0.100e127.0.0.101. - Il servizio di bilanciamento del carico usa
127.0.0.100:80e127.0.0.101:80per creare connessioni ed effettuare chiamate gRPC.
DnsResolverFactory
DnsResolverFactory Crea un sistema di risoluzione progettato per ottenere gli indirizzi da un'origine esterna. La risoluzione DNS viene comunemente usata per bilanciare il carico sulle istanze di pod che dispongono di servizi headless 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" });
Il codice precedente:
- Configura il canale creato con l'indirizzo
dns:///my-example-host.- Lo schema esegue il
dnsmapping aDnsResolverFactory. -
my-example-hostè il nome host da risolvere. - Nessuna porta specificata nell'indirizzo, quindi le chiamate gRPC vengono inviate alla porta 80. Questa è la porta predefinita per i canali non protetti. È possibile specificare una porta facoltativamente dopo il nome host. Ad esempio,
dns:///my-example-host:8080configura le chiamate gRPC da inviare alla porta 8080.
- Lo schema esegue il
- Non specifica un servizio di bilanciamento del carico. Per impostazione predefinita, il canale è un servizio di bilanciamento del carico pick first.
- Avvia la chiamata
SayHellogRPC :- Il resolver DNS ottiene gli indirizzi per il nome
my-example-hosthost . - Selezionare il primo servizio di bilanciamento del carico tenta di connettersi a uno degli indirizzi risolti.
- La chiamata viene inviata al primo indirizzo a cui si connette correttamente il canale.
- Il resolver DNS ottiene gli indirizzi per il nome
Memorizzazione nella cache degli indirizzi DNS
Le prestazioni sono importanti durante il bilanciamento del carico. La latenza di risoluzione degli indirizzi viene eliminata dalle chiamate gRPC memorizzando nella cache gli indirizzi. Un resolver verrà richiamato quando si effettua la prima chiamata gRPC e le chiamate successive usano la cache.
Gli indirizzi vengono aggiornati automaticamente se una connessione viene interrotta. L'aggiornamento è importante negli scenari in cui gli indirizzi cambiano in fase di esecuzione. Ad esempio, in Kubernetes un pod riavviato attiva il resolver DNS per aggiornare e ottenere il nuovo indirizzo del pod.
Per impostazione predefinita, un sistema di risoluzione DNS viene aggiornato se una connessione viene interrotta. Il sistema di risoluzione DNS può anche essere aggiornato facoltativamente in base a un intervallo periodico. Ciò può essere utile per rilevare rapidamente nuove istanze di pod.
services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
Il codice precedente crea un oggetto DnsResolverFactory con un intervallo di aggiornamento e lo registra con l'inserimento delle dipendenze. Per altre informazioni sull'uso di un resolver configurato personalizzato, vedere Configurare resolver e servizi di bilanciamento del carico personalizzati.
StaticResolverFactory
Un sistema di risoluzione statico viene fornito da StaticResolverFactory. Questo sistema di risoluzione:
- Non chiama un'origine esterna. L'app client configura invece gli indirizzi.
- È progettato per situazioni in cui un'app conosce già gli indirizzi che chiama.
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);
Il codice precedente:
- Crea un oggetto
StaticResolverFactory. Questa factory conosce due indirizzi:localhost:80elocalhost:81. - Registra la factory con inserimento delle dipendenze( DI).
- Configura il canale creato con:
- Indirizzo
static:///my-example-host. Lo schema esegue ilstaticmapping a un sistema di risoluzione statico. - Imposta
GrpcChannelOptions.ServiceProvidercon il provider di servizi di inserimento delle dipendenze.
- Indirizzo
In questo esempio viene creato un nuovo ServiceCollection oggetto per l'inserimento delle dipendenze. Si supponga che un'app abbia già la configurazione dell'inserimento delle dipendenze, ad esempio un sito Web di ASP.NET Core. In tal caso, i tipi devono essere registrati con l'istanza di inserimento delle dipendenze esistente.
GrpcChannelOptions.ServiceProvider viene configurato recuperando un'istanza dall'inserimento delle dipendenze IServiceProvider .
Configurare il bilanciamento del carico
Un servizio di bilanciamento del carico viene specificato in un service config oggetto utilizzando la ServiceConfig.LoadBalancingConfigs raccolta . Due servizi di bilanciamento del carico sono incorporati ed eseguono il mapping ai nomi di configurazione del servizio di bilanciamento del carico:
| Nome | Tipo | Descrizione |
|---|---|---|
pick_first |
PickFirstLoadBalancerFactory |
Tenta di connettersi agli indirizzi fino a quando non viene stabilita una connessione. Tutte le chiamate gRPC vengono effettuate alla prima connessione riuscita. |
round_robin |
RoundRobinLoadBalancerFactory |
Tenta di connettersi a tutti gli indirizzi. Le chiamate gRPC vengono distribuite in tutte le connessioni riuscite usando la logica round robin . |
service config è un'abbreviazione della configurazione del servizio ed è rappresentata dal ServiceConfig tipo . Esistono due modi per ottenere un service config canale con un servizio di bilanciamento del carico configurato:
- Un'app può specificare un oggetto
service configquando viene creato un canale usandoGrpcChannelOptions.ServiceConfig. - In alternativa, un sistema di risoluzione può risolvere un oggetto
service configper un canale. Questa funzionalità consente a un'origine esterna di specificare il modo in cui i chiamanti devono eseguire il bilanciamento del carico. Indica se un resolver supporta la risoluzione di unservice configoggetto dipende dall'implementazione del resolver. Disabilitare questa funzionalità conGrpcChannelOptions.DisableResolverServiceConfig. - Se non viene specificato alcun valore
service configoservice configse non è configurato un servizio di bilanciamento del carico, per impostazione predefinita il canale èPickFirstLoadBalancerFactory.
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" });
Il codice precedente:
- Specifica un oggetto
RoundRobinLoadBalancerFactoryinservice config. - Avvia la chiamata
SayHellogRPC :-
DnsResolverFactorycrea un sistema di risoluzione che ottiene gli indirizzi per il nomemy-example-hosthost . - Il servizio di bilanciamento del carico round robin tenta di connettersi a tutti gli indirizzi risolti.
- Le chiamate gRPC vengono distribuite in modo uniforme usando la logica round robin.
-
Configurare le credenziali del canale
Un canale deve sapere se le chiamate gRPC vengono inviate usando la sicurezza del trasporto.
http e https non fanno più parte dell'indirizzo, lo schema ora specifica un sistema di risoluzione, quindi Credentials deve essere configurato nelle opzioni del canale quando si usa il bilanciamento del carico.
-
ChannelCredentials.SecureSsl- le chiamate gRPC sono protette con Transport Layer Security (TLS). Equivalente a unhttpsindirizzo. -
ChannelCredentials.Insecure- le chiamate gRPC non usano la sicurezza del trasporto. Equivalente a unhttpindirizzo.
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" });
Usare il bilanciamento del carico con la factory client gRPC
La factory client gRPC può essere configurata per l'uso del bilanciamento del carico:
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();
Il codice precedente:
- Configura il client con un indirizzo di bilanciamento del carico.
- Specifica le credenziali del canale.
- Registra i tipi di inserimento delle dipendenze con l'oggetto dell'app IServiceCollection.
Scrivere resolver personalizzati e servizi di bilanciamento del carico
Il bilanciamento del carico lato client è estendibile:
- Implementare
Resolverper creare un sistema di risoluzione personalizzato e risolvere gli indirizzi da una nuova origine dati. - Implementare
LoadBalancerper creare un servizio di bilanciamento del carico personalizzato con un nuovo comportamento di bilanciamento del carico.
Importante
Le API usate per estendere il bilanciamento del carico lato client sono sperimentali. Possono cambiare senza preavviso.
Creare un sistema di risoluzione personalizzato
Un sistema di risoluzione:
- Implementa
Resolvere viene creato da un oggettoResolverFactory. Creare un sistema di risoluzione personalizzato implementando questi tipi. - È responsabile della risoluzione degli indirizzi usati da un servizio di bilanciamento del carico.
- Facoltativamente, è possibile specificare una configurazione del servizio.
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);
}
}
Nel codice precedente:
-
FileResolverFactoryimplementaResolverFactory. Esegue ilfilemapping allo schema e creaFileResolveristanze. -
FileResolverimplementaPollingResolver.PollingResolverè un tipo di base astratto che semplifica l'implementazione di un resolver con logica asincrona eseguendo l'override diResolveAsync. - In
ResolveAsync:- L'URI del file viene convertito in un percorso locale. Ad esempio,
file:///c:/addresses.jsondiventac:\addresses.json. - JSON viene caricato dal disco e convertito in una raccolta di indirizzi.
- Il listener viene chiamato con i risultati per informare il canale che gli indirizzi sono disponibili.
- L'URI del file viene convertito in un percorso locale. Ad esempio,
Creare un servizio di bilanciamento del carico personalizzato
Un servizio di bilanciamento del carico:
- Implementa
LoadBalancere viene creato da un oggettoLoadBalancerFactory. Creare un servizio di bilanciamento del carico personalizzato e una factory implementando questi tipi. - Vengono assegnati indirizzi da un sistema di risoluzione e vengono create
Subchannelistanze. - Tiene traccia dello stato della connessione e crea un oggetto
SubchannelPicker. Il canale usa internamente la selezione per selezionare gli indirizzi durante l'esecuzione di chiamate gRPC.
è SubchannelsLoadBalancer :
- Classe di base astratta che implementa
LoadBalancer. - Gestisce la creazione di
Subchannelistanze da indirizzi. - Semplifica l'implementazione di un criterio di selezione personalizzato su una raccolta di sottocanali.
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);
}
}
Nel codice precedente:
-
RandomBalancerFactoryimplementaLoadBalancerFactory. Esegue il mapping al nome delrandomcriterio e creaRandomBalanceristanze. -
RandomBalancerimplementaSubchannelsLoadBalancer. Crea un oggettoRandomPickerche seleziona in modo casuale un sottocanale.
Configurare resolver personalizzati e servizi di bilanciamento del carico
I resolver personalizzati e i servizi di bilanciamento del carico devono essere registrati con inserimento delle dipendenze (DI) quando vengono usati. Sono disponibili due opzioni:
- Se un'app usa già l'inserimento delle dipendenze, ad esempio un'app Web ASP.NET Core, può essere registrata con la configurazione di inserimento delle dipendenze esistente. Un IServiceProvider oggetto può essere risolto dall'inserimento di dipendenze e passato al canale tramite
GrpcChannelOptions.ServiceProvider. - Se un'app non usa l'inserimento delle dipendenze, creare:
- Oggetto ServiceCollection con tipi registrati con esso.
- Provider di servizi che usa 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);
Il codice precedente:
- Crea un oggetto
ServiceCollectione registra nuove implementazioni del sistema di risoluzione e del bilanciamento del carico. - Crea un canale configurato per l'uso delle nuove implementazioni:
-
ServiceCollectionè integrato in unIServiceProvideroggetto e impostato suGrpcChannelOptions.ServiceProvider. - L'indirizzo del canale è
file:///c:/data/addresses.json. Lo schema esegue ilfilemapping aFileResolverFactory. -
service configil nome del servizio di bilanciamento del carico èrandom. Esegue il mapping aRandomLoadBalancerFactory.
-
Perché il bilanciamento del carico è importante
HTTP/2 multiplexes multiple calls on a single TCP connection (HTTP/2 multiplexes on a single TCP connection). Se gRPC e HTTP/2 vengono usati con un servizio di bilanciamento del carico di rete (NLB), la connessione viene inoltrata a un server e tutte le chiamate gRPC vengono inviate a tale server. Le altre istanze del server nel bilanciamento carico di rete sono inattive.
I servizi di bilanciamento del carico di rete sono una soluzione comune per il bilanciamento del carico perché sono veloci e leggeri. Ad esempio, Kubernetes usa per impostazione predefinita un servizio di bilanciamento del carico di rete per bilanciare le connessioni tra istanze di pod. Tuttavia, i servizi di bilanciamento del carico di rete non sono efficaci durante la distribuzione del carico quando vengono usati con gRPC e HTTP/2.
Bilanciamento del carico sul lato proxy o sul lato client?
GRPC e HTTP/2 possono essere efficacemente bilanciati tramite un proxy del servizio di bilanciamento del carico dell'applicazione o il bilanciamento del carico lato client. Entrambe queste opzioni consentono la distribuzione di singole chiamate gRPC tra i server disponibili. La scelta tra il bilanciamento del carico sul lato client e il proxy è una scelta architetturale. Ci sono vantaggi e svantaggi per ognuno.
Proxy: le chiamate gRPC vengono inviate al proxy, il proxy prende una decisione di bilanciamento del carico e la chiamata gRPC viene inviata all'endpoint finale. Il proxy è responsabile della conoscenza degli endpoint. L'uso di un proxy aggiunge:
- Un hop di rete aggiuntivo per le chiamate gRPC.
- Latenza e utilizzo di risorse aggiuntive.
- Il server proxy deve essere configurato e configurato correttamente.
Bilanciamento del carico lato client: il client gRPC prende una decisione di bilanciamento del carico all'avvio di una chiamata gRPC. La chiamata gRPC viene inviata direttamente all'endpoint finale. Quando si usa il bilanciamento del carico lato client:
- Il client è responsabile della conoscenza degli endpoint disponibili e delle decisioni di bilanciamento del carico.
- È necessaria una configurazione client aggiuntiva.
- Le chiamate gRPC con bilanciamento del carico ad alte prestazioni eliminano la necessità di un proxy.