Serialisatie van onveranderbare typen in Orleans
Orleans heeft een functie die kan worden gebruikt om een deel van de 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 context waar deze relevant is.
Serialisatie in Orleans
Wanneer een graanmethode wordt aangeroepen, maakt de Orleans runtime een diepe kopie van de methodeargumenten en vormt de aanvraag uit de 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 op 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 u aan de ontvangstzijde twee keer een lijst met hetzelfde object, in plaats van met twee objecten met dezelfde waarden erin.
Kopiëren optimaliseren
In veel gevallen is diep kopiëren niet nodig. Een mogelijk scenario is bijvoorbeeld een webfront-end die 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 wanneer deze is doorgegeven aan het graan; in het bijzonder wordt de matrix niet opnieuw gebruikt om een toekomstige aanvraag te ontvangen. Binnen het graan wordt de bytematrix geparseerd om de invoergegevens op te halen, maar niet gewijzigd. De korrel retourneert een andere bytematrix die is gemaakt om terug te worden doorgegeven aan de webclient; de matrix wordt verwijderd zodra deze wordt 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 zelf niet achterhalen, omdat het niet kan zien of de matrices later worden gewijzigd door de webfront-end of door het graan. In de beste van alle mogelijke werelden hebben we een soort .NET-mechanisme om aan te geven dat een waarde niet meer wordt gewijzigd; Daar ontbreken we -specifieke mechanismen voor: Orleansde Immutable<T> wrapperklasse en de ImmutableAttribute.
Gebruik het [Immutable]
kenmerk om een type, parameter, eigenschap of veld als onveranderbaar te maken
Voor door de gebruiker gedefinieerde typen kan deze ImmutableAttribute worden toegevoegd aan het type. Hiermee wordt de serializer geïnstrueerd Orleansom te voorkomen dat exemplaren van dit type worden gekopieerd.
Het volgende codefragment laat zien hoe u [Immutable]
een onveranderbaar type aangeeft. Dit type wordt niet gekopieerd tijdens verzending.
[Immutable]
public class MyImmutableType
{
public int MyValue { get; }
public MyImmutableType(int value)
{
MyValue = value;
}
}
Soms hebt u mogelijk geen controle over het object, bijvoorbeeld een List<int>
object dat u tussen korrels verzendt. Andere keren zijn delen van uw objecten onveranderbaar en andere delen niet. Voor deze gevallen Orleans ondersteunt u aanvullende opties.
Methodehandtekeningen kunnen per parameter worden opgenomen ImmutableAttribute :
public interface ISummerGrain : IGrain { // `values` will not be copied. ValueTask<int> Sum([Immutable] List<int> values); }
Afzonderlijke eigenschappen en velden kunnen worden gemarkeerd ImmutableAttribute om te voorkomen dat kopieën worden gemaakt 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; } }
Immutable<T>
gebruiken
De Immutable<T> wrapper-klasse wordt gebruikt 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 noch de provider van de waarde noch de ontvanger van de waarde deze in de toekomst zal wijzigen; het is geen eenzijdige toezegging, maar eerder een wederzijdse dubbele toezegging.
Als u wilt gebruiken Immutable<T>
in uw graaninterface, in plaats van door te geven T
, pass Immutable<T>
. In het bovenstaande scenario is de graanmethode bijvoorbeeld:
Task<byte[]> ProcessRequest(byte[] request);
Wat dan zou worden:
Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);
Gebruik de constructor om een Immutable<T>
te maken:
Immutable<byte[]> immutable = new(buffer);
Gebruik de eigenschap om de waarden in de onveranderbare waarden op te .Value
halen:
byte[] buffer = immutable.Value;
Onveranderbaarheid in Orleans
Onveranderbaarheid Orleansis een vrij strikte verklaring: de inhoud van het gegevensitem wordt op geen enkele manier gewijzigd die de semantische betekenis van het item kan wijzigen of die een andere thread tegelijkertijd toegang tot het item zou geven. De veiligste manier om ervoor te zorgen, is om het item helemaal niet te wijzigen: bitwise onveranderbaarheid, in plaats van logische onveranderbaarheid.
In sommige gevallen is het veilig om dit te versoepelen naar logische onveranderbaarheid, maar zorg ervoor dat de mutatiecode correct thread-safe is. Omdat het omgaan met multithreading complex is en ongebruikelijk is in een Orleans context, raden we u ten zeerste aan deze benadering aan te houden en bitwise onveranderbaarheid aan te houden.