Сериализация неизменяемых типов в Orleans
Orleans имеет функцию, которую можно использовать, чтобы избежать некоторых расходов, связанных с сериализированием сообщений, содержащих неизменяемые типы. В этом разделе описывается функция и его приложение, начиная с контекста, в котором она относится.
Сериализация в Orleans
При вызове Orleans метода зерна среда выполнения создает глубокую копию аргументов метода и формирует запрос из копий. Это защищает от вызывающего кода, изменяющего объекты аргументов, прежде чем данные передаются в вызываемую зерна.
Если вызываемое зерно находится в другом сило, копии в конечном итоге сериализуются в поток байтов и отправляются по сети в целевой silo, где они десериализируются обратно в объекты. Если вызываемая зерна находится в одном и том же сило, копии передаются непосредственно в вызываемом методе.
Возвращаемые значения обрабатываются так же: сначала копируются, а затем могут быть сериализованы и десериализированы.
Обратите внимание, что все 3 процесса, копирование, сериализация и десериализация, уважение удостоверения объекта. Другими словами, если вы передаете список с одинаковым объектом в нем дважды, на принимающей стороне вы получите список с тем же объектом в нем дважды, а не с двумя объектами с одинаковыми значениями в них.
Оптимизация копирования
Во многих случаях глубокое копирование не требуется. Например, возможный сценарий — это веб-интерфейс, который получает массив байтов от своего клиента и передает этот запрос, включая массив байтов, на зерно для обработки. Интерфейсный процесс не делает ничего с массивом после того, как он передал его в зерно; В частности, он не использует массив для получения будущего запроса. Внутри зерна массив байтов анализируется для получения входных данных, но не измененных. Зерно возвращает другой массив байтов, созданный для возврата в веб-клиент; он не карта возвращает массив, как только он возвращает его. Интерфейс веб-интерфейса передает результирующий массив байтов клиенту без изменений.
В таком сценарии нет необходимости копировать массивы байтов запроса или ответа. К сожалению, среда выполнения не может понять это самостоятельно, Orleans так как она не может определить, изменяются ли массивы позже на веб-интерфейсе или с помощью зерна. В лучшем из всех возможных миров у нас был бы какой-то механизм .NET для указания того, что значение больше не изменено; Не хватает этого, мы добавили Orleansспециальные механизмы для этого: Immutable<T> класс-оболочка и класс ImmutableAttribute.
[Immutable]
Используйте атрибут, чтобы сделать тип, параметр, свойство или поле неизменяемым
Для определяемых пользователем типов ImmutableAttribute можно добавить в тип. Это указывает Orleansсериализатору , чтобы избежать копирования экземпляров этого типа.
Следующий фрагмент кода демонстрирует использование [Immutable]
для обозначения неизменяемого типа. Этот тип не будет скопирован во время передачи.
[Immutable]
public class MyImmutableType
{
public int MyValue { get; }
public MyImmutableType(int value)
{
MyValue = value;
}
}
Иногда у вас может не быть контроля над объектом, например, это может быть List<int>
то, что вы отправляете между зернами. В других случаях, возможно, части ваших объектов неизменяемы и другие части не являются. В таких случаях Orleans поддерживается дополнительные параметры.
Подписи методов могут включаться ImmutableAttribute на основе каждого параметра:
public interface ISummerGrain : IGrain { // `values` will not be copied. ValueTask<int> Sum([Immutable] List<int> values); }
Отдельные свойства и поля можно пометить как ImmutableAttribute предотвратить копирование копий при копировании экземпляров содержащего типа.
[GenerateSerializer] public sealed class MyType { [Id(0), Immutable] public List<int> ReferenceData { get; set; } [Id(1)] public List<int> RunningTotals { get; set; } }
Использование Immutable<T>
Immutable<T> Класс-оболочка используется для указания того, что значение может считаться неизменяемым; то есть базовое значение не будет изменено, поэтому для безопасного общего доступа не требуется копирование. Обратите внимание, что использование Immutable<T>
подразумевает, что ни поставщик значения, ни получатель значения не изменят его в будущем; это не односторонняя приверженность, а не взаимное двойное обязательство.
Чтобы использовать Immutable<T>
в интерфейсе зерна вместо передачи, передайтеT
Immutable<T>
. Например, в описанном выше сценарии метод зерна:
Task<byte[]> ProcessRequest(byte[] request);
Что затем станет следующим:
Task<Immutable<byte[]>> ProcessRequest(Immutable<byte[]> request);
Чтобы создать Immutable<T>
конструктор, просто используйте конструктор:
Immutable<byte[]> immutable = new(buffer);
Чтобы получить значения внутри неизменяемого, используйте .Value
свойство:
byte[] buffer = immutable.Value;
Неизменяемость в Orleans
Для Orleans"целей неизменяемость является довольно строгой инструкцией: содержимое элемента данных не будет изменено каким-либо образом, что может изменить семантический смысл элемента или что будет мешать другому потоку одновременно обращаться к элементу. Самый безопасный способ убедиться, что это просто не изменить элемент вообще: побитовая неизменяемость, а не логическая неизменяемость.
В некоторых случаях это безопасно для логической неизменяемости, но необходимо принять меры, чтобы обеспечить правильность мутации кода. Поскольку работа с многопоточностью сложна, и редкость в контексте Orleans , мы настоятельно рекомендуем против этого подхода и рекомендуем придерживаться битовой неизменяемости.
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по