Condividi tramite


Posizionamento granulare

Orleans garantisce che quando viene effettuata una chiamata granulare è presente un'istanza di tale granularità disponibile in memoria in alcuni server del cluster per gestire la richiesta. Se la granularità non è attualmente attiva nel cluster, Orleans seleziona uno dei server per attivare la granularità. Questo processo è detto posizionamento granulare. Il posizionamento è anche un modo per bilanciare il carico: anche la posizione dei grani occupati contribuisce addirittura al 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 casuali, preferibilmente locali e in base al carico, oppure è possibile configurare la logica personalizzata. Questo assicura la massima flessibilità nel decidere dove creare 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 sceglierà 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 RandomPlacementAttribute a un grano.

Posizionamento locale

Se il server locale è compatibile, selezionare il server locale, altrimenti selezionare un server casuale. Questa strategia di posizionamento viene configurata aggiungendo PreferLocalPlacementAttribute a un grano.

Posizionamento basato su hash

Eseguire l'hash dell'ID granulare in un numero intero non negativo e lo modula 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 non è garantito che questo 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 ID di granularità specificato. Poiché i grani posizionati usando questa strategia vengono registrati nella directory granulare, la modifica della decisione di posizionamento al variare dell'appartenenza non ha in genere un effetto evidente.

Questa strategia di posizionamento viene configurata aggiungendo HashBasedPlacementAttribute a una granularità.

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 che si prevede abbia il minor numero di attivazioni esaminando il conteggio delle attivazioni segnalate più di recente e stima il conteggio delle attivazioni corrente in base al recente numero di attivazioni effettuato dal direttore del posizionamento nel server corrente. Il direttore seleziona più server in modo casuale durante l'esecuzione di questa stima, per evitare l'overload di più server separati nello 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 Mitzenmacher, ed è usato anche in Nginx per il bilanciamento del carico distribuito, come descritto nell'articolo NGINX e l'algoritmo di bilanciamento del carico "Power of Two Choices".

Questa strategia di posizionamento viene configurata aggiungendo ActivationCountBasedPlacementAttribute a una granularità.

Posizionamento del ruolo di lavoro senza stato

Il posizionamento del ruolo di lavoro senza stato è una strategia speciale di posizionamento utilizzata dai grani del ruolo di lavoro senza stato. Questo posizionamento funziona quasi in modo identico aPreferLocalPlacement, ad eccezione del fatto che ogni server può avere più attivazioni dello stesso grano e il grano non è registrato nella directory granulare perché non è necessario.

Questa strategia di posizionamento viene configurata aggiungendo StatelessWorkerAttribute a una granularità.

Posizionamento basato su ruoli silo

Strategia di posizionamento deterministica che inserisce grani su silo con un ruolo specifico. Questa strategia di posizionamento viene configurata aggiungendo SiloRoleBasedPlacementAttribute a una granularità.

Posizionamento ottimizzato per le risorse

La strategia di posizionamento ottimizzata per le risorse cerca 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 risorse diverse.

Inoltre, questa strategia di posizionamento espone un'opzione per creare una preferenza più forte al silo locale (quella che ha ottenuto la richiesta di effettuare un nuovo posizionamento) da selezionare come destinazione per l'attivazione. Questo viene controllato tramite la proprietà LocalSiloPreferenceMargin che fa parte delle opzioni.

Inoltre, un online, algoritmo adattivo fornisce un effetto di smoothing che evita le cadute rapide del segnale trasformandolo in un processo di decadimento di tipo polinomiale. Questo è particolarmente importante per l'utilizzo della CPU e contribuisce a evitare la saturazione delle risorse sui silos, soprattutto quelli di recente adesione.

Questo algoritmo si basa su: Posizionamento basato su risorse con filtro Kalman a doppia modalità cooperativa

Questa strategia di posizionamento viene configurata aggiungendo ResourceOptimizedPlacementAttribute a una granularità.

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.
    • Finestra temporale scorrevole delle attivazioni attive negli ultimi secondi, talvolta denominata working set di attivazione.

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 una granularità

La strategia di posizionamento per un tipo di granularità viene configurata aggiungendo l'attributo appropriato nella classe grano. Gli attributi pertinenti vengono specificati nelle sezioni strategie di posizionamento.

Strategia di posizionamento personalizzata di esempio

Definire prima di tutto una classe che implementa l'interfaccia IPlacementDirector, che richiede un singolo metodo. In questo esempio si presuppone che sia presente una funzione GetSiloNumber definita che restituirà un numero silo in base a Guid del grano da creare.

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, registrare la strategia quando si compila 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.AddSingletonNamedService<
        PlacementStrategy, SamplePlacementStrategy>(
            nameof(SamplePlacementStrategy));

    services.AddSingletonKeyedService<
        Type, IPlacementDirector, SamplePlacementStrategyFixedSiloDirector>(
            typeof(SamplePlacementStrategy));
}

Per un secondo esempio semplice che mostra un ulteriore uso del contesto di posizionamento, fare riferimento a PreferLocalPlacementDirector nel Orleansrepository di origine