Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Orleans dispose d’une fonctionnalité que vous pouvez utiliser pour éviter une surcharge associée à la sérialisation des messages contenant des types immuables. Cette section décrit la fonctionnalité et son application, en commençant par le contexte dans lequel il est pertinent.
Sérialisation dans Orleans
Lorsque vous appelez une méthode de grain, le Orleans runtime effectue une copie complète des arguments de la méthode et forme la requête à partir de ces copies. Cela protège contre le code appelant qui modifie les objets d’argument avant que les données ne passent au grain appelé.
Si le grain appelé se trouve sur un autre silo, les copies sont finalement sérialisées dans un flux d’octets et envoyées sur le réseau au silo cible, où elles sont désérialisées en objets. Si le grain appelé se trouve sur le même silo, les copies sont transmises directement à la méthode appelée.
Les valeurs de retour sont gérées de la même façon : d’abord copiées, puis sérialisées et désérialisées.
Notez que les trois processus (copie, sérialisation et désérialisation) respectent l’identité d’objet. En d’autres termes, si vous passez une liste contenant le même objet deux fois, le côté récepteur obtient une liste avec le même objet deux fois, plutôt que deux objets avec les mêmes valeurs.
Optimiser la copie
Dans de nombreux cas, la copie approfondie n’est pas nécessaire. Par exemple, envisagez un scénario où une interface web reçoit un tableau d’octets de son client et transmet cette requête, avec le tableau d’octets, à un grain pour traitement. Le processus frontal ne fait rien avec le tableau après l’avoir transmis au grain ; plus précisément, il ne réutilise pas le tableau pour les demandes futures. À l’intérieur du grain, le tableau d’octets est analysé pour extraire les données d’entrée, mais n’est pas modifié. Le grain retourne un autre tableau d’octets qu’il a créé au client web et ignore le tableau immédiatement après son retour. Le serveur frontal web transmet le tableau d’octets de résultat à son client sans modification.
Dans ce scénario, il n’est pas nécessaire de copier les tableaux d’octets de requête ou de réponse. Malheureusement, le runtime Orleans ne peut pas résoudre cela automatiquement, car il ne peut pas déterminer si c'est le front-end web ou le grain qui modifie les tableaux par la suite. Dans l’idéal, un mécanisme .NET indiquerait qu’une valeur n’est plus modifiée. En l'absence de cela, nous avons ajouté des mécanismes spécifiques à Orleans : la classe Immutable<T> wrapper et le ImmutableAttribute.
Utilisez l’attribut [Immutable] pour marquer un type, un paramètre, une propriété ou un champ comme immuable
Pour les types définis par l'utilisateur, vous pouvez ajouter ImmutableAttribute au type. Cela indique au Orleans sérialiseur d’éviter de copier des instances de ce type. L’extrait de code suivant illustre l’utilisation [Immutable] pour désigner un type immuable. Ce type ne sera pas copié pendant la transmission.
[Immutable]
public class MyImmutableType
{
public int MyValue { get; }
public MyImmutableType(int value)
{
MyValue = value;
}
}
Parfois, vous risquez de ne pas contrôler l’objet ; par exemple, il peut s’agir d’un List<int> que vous envoyez entre des grains. D’autres fois, les parties de vos objets peuvent être immuables alors que d’autres ne le sont pas. Pour ces cas, Orleans prend en charge des options supplémentaires.
Les signatures de méthode peuvent inclure ImmutableAttribute par paramètre :
public interface ISummerGrain : IGrain { // `values` will not be copied. ValueTask<int> Sum([Immutable] List<int> values); }Marquez les propriétés et les champs individuels avec ImmutableAttribute pour empêcher les copies lorsque des instances du type conteneur sont copiées.
[GenerateSerializer] public sealed class MyType { [Id(0), Immutable] public List<int> ReferenceData { get; set; } [Id(1)] public List<int> RunningTotals { get; set; } }
Utilisez Immutable<T>.
Utilisez la Immutable<T> classe wrapper pour indiquer qu’une valeur peut être considérée comme immuable ; autrement dit, la valeur sous-jacente n’est pas modifiée. Par conséquent, aucune copie n’est requise pour le partage sécurisé. Notez que l’utilisation Immutable<T> n’implique ni le fournisseur ni le destinataire de la valeur ne le modifieront à l’avenir. C’est un engagement mutuel, double-côté, pas un engagement unidirecteur.
Pour utiliser Immutable<T> dans votre interface de grain, passez Immutable<T> au lieu de T. Par exemple, dans le scénario décrit ci-dessus, la méthode de traitement du grain était :
Task<byte[]> ProcessRequest(byte[] request);
Ce qui deviendra alors :
Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);
Pour créer un Immutable<T>, utilisez simplement son constructeur :
Immutable<byte[]> immutable = new(buffer);
Pour obtenir la valeur à l’intérieur du wrapper immuable, utilisez la propriété .Value :
byte[] buffer = immutable.Value;
Immuabilité dans Orleans
À des fins de Orleans, l’immuabilité est une affirmation stricte : le contenu de l’élément de données ne sera pas modifié d’aucune façon pouvant changer sa signification sémantique ou interférer avec un autre thread qui y accède simultanément. Le moyen le plus sûr de s’assurer que cela n’est simplement pas de modifier l’élément du tout : utilisez l’immuabilité au niveau du bit plutôt que l’immuabilité logique.
Dans certains cas, il est sûr d'assouplir cela vers une immuabilité logique, mais vous devez vous assurer que le code modifiant est sûrement sécurisé pour les threads. Étant donné que la gestion du multithreading est complexe et rare dans un Orleans contexte, nous vous déconseillons vivement cette approche et vous conseillons de vous en tenir à l’immuabilité au niveau binaire.