Delen via


Serialisatie van onveranderbare typen in Orleans

Orleans heeft een functie die u kunt gebruiken om enige overhead te voorkomen die is gekoppeld aan het serialiseren van berichten met onveranderbare typen. In deze sectie worden de functie en de bijbehorende toepassing beschreven, te beginnen met de context waar deze relevant is.

Serialisatie in Orleans

Wanneer u een grain-methode aanroept, maakt de Orleans runtime een diepe kopie van de methodeargumenten en wordt de aanvraag samengesteld uit deze kopieën. Dit beschermt tegen de aanroepende code die de argumentobjecten wijzigt voordat de gegevens worden doorgegeven aan het aangeroepen graan.

Als het aangeroepen graan zich in een andere silo bevindt, worden de kopieën uiteindelijk geserialiseerd in een bytestroom en via het netwerk verzonden naar de doelsilo, waar ze weer in objecten worden gedeserialiseerd. Als het aangeroepen graan zich op dezelfde silo bevindt, worden de kopieën rechtstreeks aan de aangeroepen methode overhandigd.

Retourwaarden worden op dezelfde manier verwerkt: eerst gekopieerd en vervolgens mogelijk geserialiseerd en gedeserialiseerd.

Houd er rekening mee dat alle drie processen( kopiëren, serialiseren en deserialiseren) objectidentiteit respecteren. Met andere woorden, als u een lijst met hetzelfde object twee keer doorgeeft, krijgt de ontvangende zijde twee keer een lijst met hetzelfde object, in plaats van twee objecten met dezelfde waarden.

Kopiëren optimaliseren

In veel gevallen is diep kopiëren niet nodig. Denk bijvoorbeeld aan een scenario waarin een webfront-end een bytematrix van de client ontvangt en die aanvraag, inclusief de bytematrix, doorgeeft aan een korrel voor verwerking. Het front-endproces doet niets met de matrix nadat deze aan het graan is doorgegeven; in het bijzonder wordt de matrix niet opnieuw gebruikt voor toekomstige aanvragen. In het graan wordt de bytematrix geparseerd om invoergegevens op te halen, maar wordt niet gewijzigd. De korrel retourneert een andere bytematrix die is gemaakt naar de webclient en verwijdert de matrix onmiddellijk nadat deze is geretourneerd. De webfront-end geeft de resultaat-bytematrix weer door aan de client zonder wijziging.

In een dergelijk scenario hoeft u de aanvraag- of antwoord-bytematrices niet te kopiëren. Helaas kan de Orleans runtime dit niet automatisch achterhalen, omdat het niet kan bepalen of de webfront-end of de korrel de matrices later wijzigt. Idealiter geeft een .NET-mechanisme aan dat een waarde niet meer wordt gewijzigd. Dat ontbreekt, hebben we -specifieke mechanismen toegevoegd Orleans: de Immutable<T> wrapperklasse en de ImmutableAttribute.

Gebruik het ImmutableAttribute kenmerk om een type, parameter, eigenschap of veld als onveranderbaar te markeren

Voor door de gebruiker gedefinieerde typen kunt u het ImmutableAttribute aan het type toevoegen. Hiermee wordt de Orleans serializer geïnstrueerd om te voorkomen dat exemplaren van dit type worden gekopieerd. Het volgende codefragment laat zien hoe u ImmutableAttribute een onveranderbaar type aangeeft. Dit type wordt niet gekopieerd tijdens de verzending.

[Immutable]
public class MyImmutableType
{
    public int MyValue { get; }

    public MyImmutableType(int value)
    {
        MyValue = value;
    }
}

Soms kunt u het object niet beheren; het kan bijvoorbeeld een List<int> zijn dat u tussen korrels verzendt. In andere gevallen kunnen delen van uw objecten onveranderbaar zijn, terwijl anderen dat niet zijn. In deze gevallen ondersteunt Orleans extra opties.

  1. Methodehandtekeningen kunnen per parameter worden opgenomen ImmutableAttribute :

    public interface ISummerGrain : IGrain
    {
      // `values` will not be copied.
      ValueTask<int> Sum([Immutable] List<int> values);
    }
    
  2. Markeer afzonderlijke eigenschappen en velden ImmutableAttribute om kopieën te voorkomen wanneer exemplaren van het betreffende type worden gekopieerd.

    [GenerateSerializer]
    public sealed class MyType
    {
        [Id(0), Immutable]
        public List<int> ReferenceData { get; set; }
    
        [Id(1)]
        public List<int> RunningTotals { get; set; }
    }
    

Gebruik Immutable<T>

Gebruik de Immutable<T> wrapper-klasse om aan te geven dat een waarde als onveranderbaar kan worden beschouwd. De onderliggende waarde wordt dus niet gewijzigd, dus er is geen kopie vereist voor veilig delen. Houd er rekening mee dat het gebruik Immutable<T> impliceert dat de provider en de ontvanger van de waarde deze in de toekomst niet meer zullen wijzigen. Het is een wederzijdse, dubbelzijdige toezegging, niet een eenzijdige.

Als u wilt gebruiken Immutable<T> in uw graaninterface, geeft u Immutable<T> door in plaats van T. In het hierboven beschreven scenario was de graanmethode bijvoorbeeld:

Task<byte[]> ProcessRequest(byte[] request);

Wat dan zou worden:

Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);

Om een Immutable<T> te maken, gebruik gewoon de constructor.

Immutable<byte[]> immutable = new(buffer);

Gebruik de .Value eigenschap om de waarde uit de onveranderbare wrapper te halen.

byte[] buffer = immutable.Value;

Onveranderbaarheid in Orleans

Onveranderbaarheid Orleans is een strikte bewering: de inhoud van het gegevensitem wordt niet gewijzigd op een manier die de semantische betekenis van het item kan veranderen of die kan interfereren met gelijktijdige toegang door een andere thread. De veiligste manier om ervoor te zorgen, is gewoon niet om het item helemaal te wijzigen: bitwise onveranderbaarheid gebruiken in plaats van logische onveranderbaarheid.

In sommige gevallen is het veilig om dit te versoepelen naar logische onveranderbaarheid, maar u moet ervoor zorgen dat de mutatiecode correct thread-safe is. Omdat het omgaan met multithreading complex en ongebruikelijk is in een Orleans context, raden we sterk af deze aanpak te volgen en adviseren we vast te houden aan bitwise onveranderbaarheid.