Posizionamento dei grani
Orleans garantisce che quando viene effettuata una chiamata al grain, ci sia un'istanza di quel grain disponibile in memoria su un server del cluster per gestire la richiesta. Se il grano non è attualmente attivo nel cluster, Orleans seleziona uno dei server disponibili per attivare il grano. Questo metodo viene chiamato posizionamento dei grani. Il posizionamento è anche un modo per bilanciare il carico: il posizionamento uniforme dei grani attivi aiuta a bilanciare il carico di lavoro nel cluster.
Il processo di posizionamento in Orleans è completamente configurabile: gli sviluppatori possono scegliere tra un set di criteri di posizionamento predefiniti, ad esempio criteri di posizionamento casuali, locali preferiti e basati sul carico oppure è possibile configurare la logica personalizzata. Ciò consente la massima flessibilità nel decidere dove vengono creati i grani. Ad esempio, i grani possono essere posizionati su un server vicino alle risorse su cui devono operare o vicino ad altri grani con cui comunicano. Per impostazione predefinita, Orleans selezionerà un server compatibile casuale.
La strategia di posizionamento che Orleans usa può essere configurata a livello globale o per classe granulare.
Posizionamento casuale
Un server viene selezionato in modo casuale dai server compatibili nel cluster. Questa strategia di posizionamento viene configurata aggiungendo il RandomPlacementAttribute a una grana.
Posizionamento locale
Se il server locale è compatibile, selezionare il server locale, altrimenti selezionare un server casuale. Questa strategia di posizionamento viene configurata aggiungendo il PreferLocalPlacementAttribute a un grano.
Posizionamento basato su hash
Applicare l'hash all'ID del grano per ottenere un numero intero non negativo e modularlo con il numero di server compatibili. Selezionare il server corrispondente dall'elenco dei server compatibili ordinati in base all'indirizzo del server. Si noti che questo non è garantito che rimanga stabile man mano che l'appartenenza al cluster cambia. In particolare, l'aggiunta, la rimozione o il riavvio dei server può modificare il server selezionato per un grain id specificato. Poiché i grani posizionati usando questa strategia vengono registrati nella directory dei grani, questa modifica nella decisione di posizionamento man mano che l'appartenenza cambia in genere non ha un effetto notevole.
Questa strategia di posizionamento viene configurata aggiungendo il HashBasedPlacementAttribute a un grano.
Posizionamento basato sul conteggio delle attivazioni
Questa strategia di posizionamento prevede di inserire nuove attivazioni granulari sul server meno caricato in base al numero di grani occupati di recente. Include un meccanismo in cui tutti i server pubblicano periodicamente il numero totale di attivazioni in tutti gli altri server. Il direttore di posizionamento seleziona quindi un server stimato per avere le attivazioni più poche esaminando il numero di attivazioni segnalate più di recente e stima il numero di attivazioni corrente in base al numero di attivazioni recente effettuato dal direttore di posizionamento nel server corrente. Il server director seleziona più server in modo casuale durante l'esecuzione di questa stima, per evitare che più server separati eseseguono l'overload dello stesso server. Per impostazione predefinita, due server vengono selezionati in modo casuale, ma questo valore è configurabile tramite ActivationCountBasedPlacementOptions.
Questo algoritmo si basa sulla tesi The Power of Two Choices in Randomized Load Balancing di Michael David Mitzenmachere viene usato anche in Nginx per il bilanciamento del carico distribuito, come descritto nell'articolo NGINX e "Power of Two Choices" Load-Balancing Algorithm.
Questa strategia di posizionamento viene configurata aggiungendo il ActivationCountBasedPlacementAttribute a un granulo.
Posizionamento di lavoro senza stato
Il posizionamento dei lavoratori senza stato è una strategia speciale di posizionamento usata da lavoro senza stato grani. Questo posizionamento funziona quasi in modo identico a PreferLocalPlacement, tranne che ogni server può avere più attivazioni dello stesso grano e il grano non è registrato nella directory dei grani perché non è necessario.
Questa strategia di posizionamento viene configurata aggiungendo il StatelessWorkerAttribute a un elemento.
Posizionamento basato su ruoli di silo
Strategia di posizionamento deterministica che inserisce grani su silo con un ruolo specifico. Questa strategia di posizionamento viene configurata aggiungendo il SiloRoleBasedPlacementAttribute a una granularità.
Posizionamento ottimizzato delle risorse
La strategia di posizionamento ottimizzata per le risorse tenta di ottimizzare le risorse del cluster bilanciando le attivazioni granulari tra silo in base all'utilizzo di memoria e CPU disponibile. Assegna pesi alle statistiche di runtime per assegnare priorità a risorse diverse e calcola un punteggio normalizzato per ogni silo. Il silo con il punteggio più basso viene scelto per inserire l'attivazione imminente. La normalizzazione garantisce che ogni proprietà contribuisca proporzionalmente al punteggio complessivo. I pesi possono essere modificati tramite il ResourceOptimizedPlacementOptions in base a requisiti e priorità specifici dell'utente per differenti risorse.
Inoltre, questa strategia di posizionamento rende disponibile un'opzione per creare una preferenza più forte al silo locale (quello che ha ottenuto la richiesta di effettuare un nuovo posizionamento) da selezionare come obiettivo per l'attivazione. Questo controllo viene controllato tramite la proprietà LocalSiloPreferenceMargin
che fa parte delle opzioni.
Inoltre, un online, algoritmo adattivo di fornisce un effetto di smussamento che consente di evitare perdite rapide di segnale trasformandole in un processo di decadimento simile a un polinomio. Questo è particolarmente importante per l'utilizzo della CPU e contribuisce complessivamente a evitare la saturazione delle risorse sui silo, soprattutto quelli appena uniti.
Questo algoritmo si basa su: posizionamento basato su risorse con filtro Kalman a doppia modalità cooperativa
Questa strategia di posizionamento viene configurata aggiungendo il ResourceOptimizedPlacementAttribute a un granello.
Scegliere una strategia di posizionamento
La scelta della strategia di posizionamento granulare appropriata, oltre alle impostazioni predefinite fornite da Orleans, richiede il monitoraggio e la valutazione dello sviluppatore. La scelta della strategia di posizionamento deve essere basata sulle dimensioni e sulla complessità dell'app, delle caratteristiche del carico di lavoro e dell'ambiente di distribuzione.
Il posizionamento casuale si basa sulla legge dei numeri grandi, quindi è in genere un buon valore predefinito quando c'è un carico imprevedibile distribuito in un numero elevato di grani (10.000 più).
Il posizionamento basato sul conteggio delle attivazioni ha anche un elemento casuale, basandosi sul principio Power of Two Choices, che è un algoritmo comunemente usato per il bilanciamento del carico distribuito e viene usato nei servizi di bilanciamento del carico più diffusi. I silo pubblicano spesso statistiche di runtime in altri silo nel cluster, tra cui:
- Memoria disponibile, memoria fisica totale e utilizzo della memoria.
- Utilizzo della CPU.
- Numero totale di attivazioni e numero di attivazioni attive recenti.
- Una finestra temporale scorrevole delle attivazioni degli ultimi secondi, talvolta chiamata set di lavoro delle attivazioni.
Da queste statistiche vengono attualmente usati solo i conteggi di attivazione per determinare il carico su un determinato silo.
In definitiva, è necessario sperimentare diverse strategie e monitorare le metriche delle prestazioni per determinare la scelta migliore. Selezionando la strategia di posizionamento granulare corretta, è possibile ottimizzare le prestazioni, la scalabilità e l'efficacia dei costi delle app Orleans.
Configurare la strategia di posizionamento predefinita
Orleans userà il posizionamento casuale a meno che non venga eseguito l'override del valore predefinito. La strategia di posizionamento predefinita può essere sostituita registrando un'implementazione di PlacementStrategy durante la configurazione:
siloBuilder.ConfigureServices(services =>
services.AddSingleton<PlacementStrategy, MyPlacementStrategy>());
Configurare la strategia di posizionamento per un grain
La strategia di posizionamento per un tipo di grain viene configurata aggiungendo l'attributo appropriato sulla classe grain. Gli attributi pertinenti vengono specificati nelle strategie di posizionamento delle sezioni .
Strategia di posizionamento personalizzata di esempio
Definire inizialmente una classe che implementa l'interfaccia IPlacementDirector, richiedendo un singolo metodo. In questo esempio si presuppone che sia presente una funzione GetSiloNumber
definita che restituirà un numero di silo dato il Guid del grano in fase di creazione.
public class SamplePlacementStrategyFixedSiloDirector : IPlacementDirector
{
public Task<SiloAddress> OnAddActivation(
PlacementStrategy strategy,
PlacementTarget target,
IPlacementContext context)
{
var silos = context.GetCompatibleSilos(target).OrderBy(s => s).ToArray();
int silo = GetSiloNumber(target.GrainIdentity.PrimaryKey, silos.Length);
return Task.FromResult(silos[silo]);
}
}
È quindi necessario definire due classi per consentire l'assegnazione di classi granulari alla strategia:
[Serializable]
public sealed class SamplePlacementStrategy : PlacementStrategy
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class SamplePlacementStrategyAttribute : PlacementAttribute
{
public SamplePlacementStrategyAttribute() :
base(new SamplePlacementStrategy())
{
}
}
Contrassegnare quindi tutte le classi di granularità che si vuole usare con l'attributo :
[SamplePlacementStrategy]
public class MyGrain : Grain, IMyGrain
{
// ...
}
Infine, registra la strategia quando costruisci il SiloHost:
private static async Task<ISiloHost> StartSilo()
{
var builder = new HostBuilder(c =>
{
// normal configuration methods omitted for brevity
c.ConfigureServices(ConfigureServices);
});
var host = builder.Build();
await host.StartAsync();
return host;
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddPlacementDirector<SamplePlacementStrategy, SamplePlacementStrategyFixedSiloDirector>();
}
Per un secondo esempio semplice che mostra un ulteriore uso del contesto di posizionamento, vedere il PreferLocalPlacementDirector
nel repository di origine Orleans.