Identità granulare
I grani in Orleans hanno ognuno un singolo identificatore univoco e definito dall'utente costituito da due parti:
- Il nome del tipo di granularità, che identifica in modo univoco la classe granulare.
- La chiave granulare, che identifica in modo univoco un'istanza logica di tale classe di granularità.
Il tipo di granularità e la chiave sono entrambi rappresentati come stringhe leggibili in Orleans e, per convenzione, l'identità granulare viene scritta con il tipo di granularità e la chiave separati da un carattere /
. Ad esempio, shoppingcart/bob65
rappresenta il tipo di granularità denominato shoppingcart
con una chiave bob65
.
Non è comune costruire direttamente identità granulari. È invece più comune creare riferimenti granulari usando Orleans.IGrainFactory.
Le sezioni seguenti illustrano in modo più dettagliato i nomi dei tipi di granularità e le chiavi granulari.
Nomi dei tipi di granularità
Orleans crea automaticamente un nome di tipo granulare basato sulla classe di implementazione granulare rimuovendo il suffisso "Grain" dal nome della classe, se presente, e convertendo la stringa risultante nella relativa rappresentazione in lettere minuscole. Ad esempio, a una classe denominata ShoppingCartGrain
verrà assegnato il nome del tipo di granularità shoppingcart
. È consigliabile che i nomi e le chiavi dei tipi di granularità siano costituiti solo da caratteri stampabili, ad esempio alfanumerici (a
-z
, A
-Z
e 0
-9
) e simboli, ad esempio -
, _
, @
, =
. Altri caratteri possono oppure no essere supportati e spesso necessitano di un trattamento speciale quando vengono stampati nei log o visualizzati come identificatori in altri sistemi, ad esempio database.
In alternativa, l'attributo Orleans.GrainTypeAttribute può essere usato per personalizzare il nome del tipo di granularità per la classe granulare a cui è associato, come nell'esempio seguente:
[GrainType("cart")]
public class ShoppingCartGrain : IShoppingCartGrain
{
// Add your grain implementation here
}
Nell'esempio precedente, la classe granulare, ShoppingCartGrain
ha un nome di tipo granulare di cart
. Ogni granularità può avere un solo nome di tipo granulare.
Per i grani generici, l'arità generica deve essere inclusa nel nome del tipo di granularità. Si consideri ad esempio la classe DictionaryGrain<K, V>
seguente:
[GrainType("dict`2")]
public class DictionaryGrain<K, V> : IDictionaryGrain<K, V>
{
// Add your grain implementation here
}
La classe granulare ha due parametri generici, quindi un backtick `
seguito dall'arità generica, 2, viene aggiunto alla fine del nome del tipo di granularità, dict
per creare il nome del tipo di granularità dict`2
, come specificato nell'attributo nella classe granulare, [GrainType("dict`2")]
.
Chiavi di granularità
Per praticità, Orleans espone metodi che consentono la costruzione di chiavi di granularità da Guid o da Int64, oltre a un oggetto String. La chiave primaria ha come ambito il tipo di granularità. Di conseguenza, l'identità completa di un grano viene formata dal tipo di granularità e dalla relativa chiave.
Il chiamante del grano decide quale schema deve essere utilizzato. Le opzioni sono:
Poiché i dati sottostanti sono gli stessi, gli schemi possono essere usati in modo intercambiabile: sono tutti codificati come stringhe.
Le situazioni che richiedono un'istanza di granularità singleton possono usare un valore fisso noto, ad esempio "default"
. Si tratta semplicemente di una convenzione, ma rispettando questa convenzione diventa chiaro nel sito chiamante che è in uso un grano singleton.
Uso di identificatori univoci globali (GUID) come chiavi
System.Guid creano chiavi utili quando si desiderano la casualità e l'univocità globale, ad esempio quando si crea un nuovo processo in un sistema di elaborazione processi. Non è necessario coordinare l'allocazione delle chiavi, che potrebbero introdurre un singolo punto di errore nel sistema o un blocco sul lato sistema su una risorsa che potrebbe presentare un collo di bottiglia. Esiste una probabilità molto bassa che i GUID collidano, quindi sono una scelta comune quando si progetta un sistema che deve allocare identificatori casuali.
Riferimento a una granularità in base al GUID nel codice client:
var grain = grainFactory.GetGrain<IExample>(Guid.NewGuid());
Recupero della chiave primaria dal codice granulare:
public override Task OnActivateAsync()
{
Guid primaryKey = this.GetPrimaryKey();
return base.OnActivateAsync();
}
Uso di numeri interi come chiavi
È disponibile anche un numero intero lungo, che avrebbe senso se la granularità fosse persistente in un database relazionale, in cui gli indici numerici sono preferiti rispetto ai GUID.
Riferimento a una granularità in base a un numero intero lungo nel codice client:
var grain = grainFactory.GetGrain<IExample>(1);
Recupero della chiave primaria dal codice granulare:
public override Task OnActivateAsync()
{
long primaryKey = this.GetPrimaryKeyLong();
return base.OnActivateAsync();
}
Uso di stringhe come chiavi
È disponibile anche una stringa.
Riferimento a una granularità in base alla stringa nel codice client:
var grain = grainFactory.GetGrain<IExample>("myGrainKey");
Recupero della chiave primaria dal codice granulare:
public override Task OnActivateAsync()
{
string primaryKey = this.GetPrimaryKeyString();
return base.OnActivateAsync();
}
Uso di chiavi composte
Se si dispone di un sistema che non si adatta bene con GUID o long, è possibile optare per una chiave primaria composta, che consente di usare una combinazione di un GUID o long e una stringa per fare riferimento a una granularità.
È possibile ereditare l'interfaccia dall’interfaccia IGrainWithGuidCompoundKey o IGrainWithIntegerCompoundKey come la seguente:
public interface IExampleGrain : Orleans.IGrainWithIntegerCompoundKey
{
Task Hello();
}
Nel codice client, questo aggiunge un secondo argomento al metodo IGrainFactory.GetGrain nella factory di granularità:
var grain = grainFactory.GetGrain<IExample>(0, "a string!", null);
Per accedere alla chiave composta nella granularità, è possibile chiamare un overload nel metodo GrainExtensions.GetPrimaryKey (GrainExtensions.GetPrimaryKeyLong):
public class ExampleGrain : Orleans.Grain, IExampleGrain
{
public Task Hello()
{
long primaryKey = this.GetPrimaryKeyLong(out string keyExtension);
Console.WriteLine($"Hello from {keyExtension}");
Task.CompletedTask;
}
}
Perché i grani usano identificatori logici
Negli ambienti orientati agli oggetti, ad esempio .NET, l'identità di un oggetto è difficile da distinguere da un riferimento. Quando un oggetto viene creato usando la parola chiave new
, il riferimento restituito rappresenta tutti gli aspetti della relativa identità, ad eccezione di quelli che eseguono il mapping dell'oggetto a un'entità esterna rappresentata. Orleans è progettato per i sistemi distribuiti. Nei sistemi distribuiti i riferimenti a oggetti non possono rappresentare l'identità dell'istanza perché i riferimenti agli oggetti sono limitati allo spazio indirizzi di un singolo processo. Orleans usa identificatori logici per evitare questa limitazione. I grani usano degli identificatori logici in modo che i riferimenti granulari rimangano validi per tutta la durata del processo e siano portabili da un processo a un altro, consentendo di archiviarli e recuperarli in un secondo momento o essere inviati attraverso una rete a un altro processo nell'applicazione, mentre fanno ancora riferimento alla stessa entità: la granularità per cui è stato creato il riferimento.