Grainplatzierung

Orleans stellt sicher, dass beim Grainaufruf eine Instanz dieses Grains auf einem Server im Cluster im Speicher verfügbar ist, um die Anforderung zu bearbeiten. Wenn das Grain derzeit nicht im Cluster aktiv ist, wählt Orleans einen der Server aus, auf dem das Grain aktiviert werden soll. Dies wird als Grainplatzierungbezeichnet. Die Platzierung ist auch eine Möglichkeit, die Last auszugleichen: Die gleichmäßige Platzierung von ausgelasteten Grains trägt dazu bei, die Workload im gesamten Cluster auszugleichen.

Der Platzierungsprozess in Orleans ist vollständig konfigurierbar: Entwickler können aus einer Reihe von vorgefertigten Richtlinien für die Platzierung auswählen, z. B. „random“ (zufällig), „prefer-local“ (lokal bevorzugen) und „load-based“ (lastbasiert), oder eine benutzerdefinierte Logik festlegen. Dies ermöglicht volle Flexibilität bei der Entscheidung, wo Grain erstellt wird. So können z. B. Grains auf einem Server in der Nähe von Ressourcen platziert werden, auf denen sie arbeiten müssen, oder in der Nähe von anderen Grains, mit denen sie kommunizieren. Standardmäßig wählt Orleans einen zufällig kompatiblen Server aus.

Die von Orleans verwendete Platzierungsstrategie kann global oder pro Grainklasse konfiguriert werden.

Zufällige Platzierung

Ein Server wird zufällig von den kompatiblen Servern im Cluster ausgewählt. Diese Platzierungsstrategie wird durch Hinzufügen der RandomPlacementAttribute-Instanz zu einem Grain konfiguriert.

Lokale Platzierung

Wenn der lokale Server kompatibel ist, wählen Sie den lokalen Server aus, andernfalls wählen Sie einen zufälligen Server. Diese Platzierungsstrategie wird durch Hinzufügen der PreferLocalPlacementAttribute-Instanz zu einem Grain konfiguriert.

Hashbasierte Platzierung

Zerlegen Sie die Grain-ID in eine nicht negative Ganzzahl, und führen Sie eine Modulo-Berechnung mit der Anzahl der kompatiblen Server durch. Wählen Sie den entsprechenden Server aus der Liste der kompatiblen Server aus, geordnet nach Serveradresse. Beachten Sie, dass dies nicht garantiert stabil bleibt, wenn sich die Clustermitgliedschaft ändert. Insbesondere das Hinzufügen, Entfernen oder Neustarten von Servern kann den für eine bestimmte Grain-ID ausgewählten Server verändern. Da Grains, die mithilfe dieser Strategie platziert werden, im Grainverzeichnis registriert werden, hat diese Änderung der Platzierungsentscheidung bei Änderungen der Mitgliedschaft in der Regel keine spürbaren Auswirkungen.

Diese Platzierungsstrategie wird durch Hinzufügen der HashBasedPlacementAttribute-Instanz zu einem Grain konfiguriert.

Platzierung auf Basis der Aktivierungsanzahl

Diese Platzierungsstrategie zielt darauf ab, neue Grainaktivierungen auf dem am wenigsten ausgelasteten Server zu platzieren, basierend auf der Anzahl der kürzlich ausgelasteten Grains. Sie umfasst einen Mechanismus, bei dem alle Server regelmäßig die Gesamtzahl ihrer Aktivierungen an alle anderen Server veröffentlichen. Der Platzierungsbeauftragte wählt dann einen Server aus, für den die wenigsten Aktivierungen vorhergesagt werden, indem er die zuletzt gemeldete Aktivierungszahl untersucht und eine Vorhersage der aktuellen Aktivierungszahl auf der Grundlage der vom Platzierungsbeauftragten auf dem aktuellen Server vorgenommenen letzten Aktivierungszahl trifft. Der Beauftragte wählt bei dieser Vorhersage mehrere Server nach dem Zufallsprinzip aus, um zu vermeiden, dass mehrere separate Server denselben Server überlasten. Standardmäßig werden zwei Server zufällig ausgewählt, aber dieser Wert kann über ActivationCountBasedPlacementOptions konfiguriert werden.

Dieser Algorithmus basiert auf der Dissertation The Power of Two Choices in Randomized Load Balancing von Michael David Mitzenmacher und wird auch in Nginx für den verteilten Lastenausgleich verwendet, wie in dem Artikel NGINX and the „Power of Two Choices“ Load-Balancing Algorithm (NGINX und der Lastenausgleichsalgorithmus „Power of Two Choices“) beschrieben.

Diese Platzierungsstrategie wird durch Hinzufügen der ActivationCountBasedPlacementAttribute-Instanz zu einem Grain konfiguriert.

Zustandslose Workerplatzierung

Zustandslose Workerplatzierung ist eine spezielle Platzierungsstrategie, die von zustandslosen Workern Grainsverwendet wird. Diese Platzierung funktioniert fast genauso wie PreferLocalPlacement, außer dass jeder Server mehrere Aktivierungen desselben Grains aufweisen kann und das Grain nicht im Grainverzeichnis registriert wird, da es nicht notwendig ist.

Diese Platzierungsstrategie wird durch Hinzufügen der StatelessWorkerAttribute-Instanz zu einem Grain konfiguriert.

Platzierung auf Basis der Silorolle

Eine deterministische Platzierungsstrategie, die Grains auf Silos mit einer bestimmten Rolle verteilt. Diese Platzierungsstrategie wird durch Hinzufügen der SiloRoleBasedPlacementAttribute-Instanz zu einem Grain konfiguriert.

Auswählen einer Platzierungsstrategie

Die Wahl der geeigneten Strategie für die Platzierung von Grains, die über die von Orleans bereitgestellten Standardeinstellungen hinausgeht, erfordert eine Überwachung und eine Auswertung durch den Entwickler. Die Wahl der Platzierungsstrategie sollte auf der Größe und Komplexität der App, der Workloadmerkmale und der Bereitstellungsumgebung basieren.

Die zufällige Platzierung beruht auf dem Gesetz der großen Zahlen, sodass sie in der Regel ein geeigneter Standard ist, wenn eine unvorhersehbare Last auf eine große Anzahl von Grains (10.000 und mehr) verteilt ist.

Die auf der Aktivierungszahl basierende Platzierung hat ebenfalls ein Zufallselement und beruht auf dem Prinzip „Power of Two Choices“. Dies ist ein häufig verwendeter Algorithmus für den verteilten Lastenausgleich und wird in gängigen Lastenausgleichssystemen verwendet. Silos veröffentlichen häufig Laufzeitstatistiken für andere Silos im Cluster, einschließlich:

  • Verfügbarer Arbeitsspeicher, gesamter physischer Speicher und Speicherauslastung.
  • CPU-Auslastung.
  • Gesamtzahl der Aktivierungen und Anzahl der letzten aktiven Aktivierungen.
    • Ein gleitendes Fenster von Aktivierungen, die in den letzten Sekunden aktiv waren, manchmal auch als Aktivierungsarbeitssatz bezeichnet.

Aus diesen Statistiken werden derzeit nur die Aktivierungszähler verwendet, um die Auslastung eines bestimmten Silos zu bestimmen.

Letztendlich sollten Sie mit verschiedenen Strategien experimentieren und die Metriken der Leistung überwachen, um die beste Lösung zu finden. Durch die Wahl der richtigen Strategie für die Grainplatzierung können Sie die Leistung, Skalierbarkeit und Kosteneffizienz Ihrer Orleans-Apps optimieren.

Konfigurieren der Standardplatzierungsstrategie

Orleans verwendet eine zufällige Platzierung, es sei denn, die Standardeinstellung wird außer Kraft gesetzt. Die Standardplatzierungsstrategie kann außer Kraft gesetzt werden, indem Sie bei der Konfiguration eine Implementierung von PlacementStrategy registrieren:

siloBuilder.ConfigureServices(services =>
    services.AddSingleton<PlacementStrategy, MyPlacementStrategy>());

Konfigurieren der Strategie für die Grainplatzierung

Die Platzierungsstrategie für einen Graintyp wird durch Hinzufügen des entsprechenden Attributs zur Grainklasse konfiguriert. Die relevanten Attribute werden in den Abschnitten über die Platzierungsstrategien angegeben.

Beispiel für eine benutzerdefinierte Platzierungsstrategie

Definieren Sie zunächst eine Klasse, die die IPlacementDirector-Schnittstelle implementiert und eine einzelne Methode erfordert. In diesem Beispiel gehen wir davon aus, dass Sie die Funktion GetSiloNumber definiert haben, die eine Silonummer mit der Guid des zu erstellenden Grains zurückgibt.

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]);
    }
}

Dann müssen Sie zwei Klassen definieren, damit der Strategie Grainklassen zugewiesen werden können:

[Serializable]
public sealed class SamplePlacementStrategy : PlacementStrategy
{
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class SamplePlacementStrategyAttribute : PlacementAttribute
{
    public SamplePlacementStrategyAttribute() :
        base(new SamplePlacementStrategy())
    {
    }
}

Dann markieren Sie einfach alle Grainklassen, die Sie mit dieser Strategie verwenden möchten, mit dem folgenden Attribut:

[SamplePlacementStrategy]
public class MyGrain : Grain, IMyGrain
{
    // ...
}

Registrieren Sie abschließend die Strategie, wenn Sie den SiloHost erstellen:

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));
}

Ein zweites einfaches Beispiel, das die weitere Verwendung des Platzierungskontexts zeigt, finden Sie unter PreferLocalPlacementDirector im Orleans-Quellrepository.