Grainidentitäten

Grains in Orleans verfügen jeweils über einen einzelnen, eindeutigen, benutzerdefinierten Bezeichner, der aus zwei Teilen besteht:

  1. Name des Graintyps, der die Grainklasse eindeutig identifiziert
  2. Grainschlüssel, der eine logische Instanz dieser Grainklasse eindeutig identifiziert

Der Graintyp und der Grainschlüssel werden als von Menschen lesbare Zeichenfolgen in Orleans dargestellt, und gemäß Konvention wird die Grainidentität mit dem Graintyp und dem Grainschlüssel getrennt durch das Zeichen / geschrieben. shoppingcart/bob65 stellt beispielsweise den Graintyp namens shoppingcart mit dem Schlüssel bob65 dar.

Es ist nicht üblich, Grainidentitäten direkt zu erstellen. Stattdessen ist es gängiger, Grainverweise mithilfe von Orleans.IGrainFactory zu erstellen.

In den folgenden Abschnitten werden Graintypnamen und Grainschlüssel ausführlicher erläutert.

Graintypnamen

Orleans erstellt einen Graintypnamen für Sie basierend auf der Grainimplementierungsklasse, indem das Suffix „Grain“ aus dem Klassennamen entfernt wird, falls vorhanden, und die resultierende Zeichenfolge in Kleinschreibung konvertiert wird. Beispielsweise erhält eine Klasse mit dem Namen ShoppingCartGrain den Graintypnamen shoppingcart. Es wird empfohlen, dass Graintypnamen und -schlüssel nur aus druckbaren Zeichen wie alphanumerischen Zeichen (a-z, A-Z und 0-9) und Symbolen wie -, _, @ und = bestehen. Andere Zeichen werden möglicherweise unterstützt und benötigen häufig eine spezielle Behandlung, wenn sie in Protokollen ausgegeben oder als Bezeichner in anderen Systemen wie Datenbanken angezeigt werden.

Alternativ kann das Attribut Orleans.GrainTypeAttribute verwendet werden, um den Graintypnamen für die Grainklasse anzupassen, der sie zugeordnet ist, wie im folgenden Beispiel gezeigt:

[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
    // Add your grain implementation here
}

Im vorherigen Beispiel hat die Grainklasse ShoppingCartGrain den Graintypnamen cart. Jedes Grain kann nur einen Graintypnamen haben.

Bei generischen Grains muss die generische Arität im Graintypnamen enthalten sein. Betrachten Sie beispielsweise die folgende DictionaryGrain<K, V>-Klasse:

[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
    // Add your grain implementation here
}

Die Grainklasse verfügt über zwei generische Parameter. Daher wird ein Backtick (`) gefolgt von der generischen Arität (2) am Ende des Graintypnamens (dict)hinzugefügt, um den Graintypnamen dict`2 zu erstellen, wie im Attribut für die Grainklasse angegeben: [GrainType("dict`2")].

Grainschlüssel

Zur Vereinfachung stellt Orleans zur Verfügung, die das Erstellen von Grainschlüsseln aus Guid oder Int64 zusätzlich zu String ermöglichen. Der Geltungsbereich des Primärschlüssels ist auf den Graintyp abgestimmt. Daher wird die vollständige Identität eines Grains aus dem Typ des Grains und seinem Schlüssel gebildet.

Der Aufrufer des Grains entscheidet, welches Schema verwendet werden soll. Die Optionen sind:

Da die zugrunde liegenden Daten identisch sind, können die Schemas gegeneinander austauschbar verwendet werden: Sie werden alle als Zeichenfolgen codiert.

Situationen, die eine Singleton-Graininstanz erfordern, können einen bekannten, festen Wert wie "default" verwenden. Dies ist lediglich eine Konvention, aber durch die Einhaltung dieser Konvention wird an der Aufrufersite deutlich, dass ein Singleton-Grain verwendet wird.

Verwenden global eindeutiger Bezeichner (GUIDs) als Schlüssel

System.Guid ergeben nützliche Schlüssel, wenn Zufälligkeit und globale Eindeutigkeit gewünscht sind, z. B. beim Erstellen eines neuen Auftrags in einem Auftragsverarbeitungssystem. Sie müssen die Zuordnung von Schlüsseln nicht koordinieren, was einen Single Point of Failure (SPOF) im System einführen oder eine systemseitige Sperre einer Ressource verursachen könnte, die einen Engpass darstellen könnte. Die Wahrscheinlichkeit, dass GUIDs in Konflikt stehen, ist sehr gering, sodass sie beim Entwerfen eines Systems, das zufällige Bezeichner zuordnen muss, eine häufige Wahl sind.

Verweisen auf ein Grain mittels GUID im Clientcode:

var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());

Abrufen des Primärschlüssels aus Graincode:

public override Task OnActivateAsync()
{
    Guid primaryKey = this.GetPrimaryKey();
    return base.OnActivateAsync();
}

Verwenden von Integerwerten als Schlüssel

Ein Long Integer ist ebenfalls verfügbar, was sinnvoll wäre, wenn das Grain dauerhaft in einer relationalen Datenbank abgelegt wird, in der numerische Indizes gegenüber GUIDs bevorzugt werden.

Verweisen auf ein Grain mittels Long Integer im Clientcode:

var grain = grainFactory.GetGrain<IExample>(1);

Abrufen des Primärschlüssels aus Graincode:

public override Task OnActivateAsync()
{
    long primaryKey = this.GetPrimaryKeyLong();
    return base.OnActivateAsync();
}

Verwenden von Zeichenfolgen als Schlüssel

Eine Zeichenfolge ist ebenfalls verfügbar.

Verweisen auf ein Grain mittels Zeichenfolge (string) im Clientcode:

var grain = grainFactory.GetGrain<IExample>("myGrainKey");

Abrufen des Primärschlüssels aus Graincode:

public override Task OnActivateAsync()
{
    string primaryKey = this.GetPrimaryKeyString();
    return base.OnActivateAsync();
}

Verwenden von Verbundschlüsseln

Wenn Sie über ein System verfügen, das nicht gut zu GUIDs oder Long-Werten passt, können Sie sich für einen zusammengesetzten Primärschlüssel entscheiden, mit dem Sie eine Kombination aus einer GUID oder einem Long Integer und einer Zeichenfolge verwenden können, um auf ein Grain zu verweisen.

Sie können Ihre Schnittstelle wie folgt von der IGrainWithGuidCompoundKey- oder IGrainWithIntegerCompoundKey-Schnittstelle erben, wie folgt:

public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
    Task Hello();
}

Im Clientcode fügt dies der IGrainFactory.GetGrain-Methode ein zweites Argument in der Grainfactory hinzu:

var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);

Um auf den zusammengesetzten Schlüssel im Grain zuzugreifen, können wir eine Überladung der GrainExtensions.GetPrimaryKey-Methode (die GrainExtensions.GetPrimaryKeyLong) aufrufen:

public class ExampleGrain : Orleans.Grain, IExampleGrain
{
    public Task Hello()
    {
        long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
        Console.WriteLine($"Hello from {keyExtension}");

        Task.CompletedTask;
    }
}

Gründe für die Verwendung logischer Bezeichner durch Grains

In objektorientierten Umgebungen wie .NET lässt sich die Identität eines Objekts nur schwer von einem Verweis darauf unterscheiden. Wenn ein Objekt mit dem Schlüsselwort new erstellt wird, stellt der Verweis, den Sie zurück erhalten, alle Aspekte seiner Identität mit Ausnahme derjenigen dar, die das Objekt einer externen Entität zuordnen, die es darstellt. Orleans ist für verteilte Systeme konzipiert. In verteilten Systemen können Objektverweise keine Instanzidentität darstellen, da Objektverweise auf den Adressraum eines einzelnen Prozesses beschränkt sind. Orleans verwendet logische Bezeichner, um diese Einschränkung zu vermeiden. Grains verwenden logische Bezeichner, sodass Grainverweise über die Prozesslebensdauer hinweg gültig sind und von einem Prozess in einen anderen portiert werden können. So können sie gespeichert und später abgerufen oder in einem Netzwerk an einen anderen Prozess in der Anwendung gesendet werden. Dabei verweisen sie immer noch auf dieselbe Entität: das Grain, für das der Verweis erstellt wurde.