Orleans Руководства по миграции

В этой статье приводятся рекомендации по миграции для обновления между основными Orleans версиями. Выберите целевую версию с помощью селектора версий выше.

Переход с Orleans версии 7.0 на 10.0

Orleans 10.0 содержит несколько новых функций, включая встроенную панель мониторинга. В этом разделе рассматриваются изменения, необходимые для миграции с Orleans 7.0 на 10.0, включая промежуточные этапы до 8.0 и 9.0.

Сводка критических изменений

Изменение, нарушающее совместимость Воздействие Миграция
AddGrainCallFilter удалено Ошибка компиляции Используйте AddIncomingGrainCallFilter
LeaseAquisitionPeriod исправлено опечатка Ошибка компиляции Используйте LeaseAcquisitionPeriod
LoadSheddingLimit Переименован Ошибка компиляции Используйте CpuThreshold
CancelRequestOnTimeout Изменено по умолчанию Поведение Можно явно установить true, если необходимо
Для поставщика ADO.NET требуется Microsoft.Data.SqlClient Ошибка компиляции и выполнения Замена System.Data.SqlClient пакета
[Unordered] атрибут устарел Предупреждение Удаление атрибута (не действует)
OrleansConstructorAttribute устаревший Предупреждение Использование GeneratedActivatorConstructorAttribute или ActivatorUtilitiesConstructorAttribute только для конструкторов, требующих внедрения зависимостей, а не для сериализованных свойств данных
RegisterTimer устаревший Предупреждение Используйте RegisterGrainTimer

Обновления пакетов

Обновите ссылки на пакет NuGet с Orleans версии 7.x до 10.0:

Orleans Пакет 7.x Orleans Пакет 10.0
Microsoft.Orleans.Server 7.x Microsoft.Orleans.Server 10.0.0
Microsoft.Orleans.Client 7.x Microsoft.Orleans.Client 10.0.0
Microsoft.Orleans.Sdk 7.x Microsoft.Orleans.Sdk 10.0.0
Microsoft.Orleans.Streaming.EventHubs 7.x Microsoft.Orleans.Streaming.EventHubs 10.0.0
Microsoft.Orleans.Streaming.AzureStorage 7.x Microsoft.Orleans.Streaming.AzureStorage 10.0.0
Microsoft.Orleans.Persistence.AzureStorage 7.x Microsoft.Orleans.Persistence.AzureStorage 10.0.0
Microsoft.Orleans.Clustering.AzureStorage 7.x Microsoft.Orleans.Clustering.AzureStorage 10.0.0

Критическое изменение: AddGrainCallFilter заменено на AddIncomingGrainCallFilter

AddGrainCallFilter Метод IServiceCollection расширения удален. Замените его на AddIncomingGrainCallFilterISiloBuilder или IClientBuilder.

// Orleans 7.x (no longer works)
services.AddGrainCallFilter(new MyFilter());
services.AddGrainCallFilter<MyFilter>();

// Orleans 10.0
siloBuilder.AddIncomingGrainCallFilter(new MyFilter());
siloBuilder.AddIncomingGrainCallFilter<MyFilter>();

// Or using a delegate
siloBuilder.AddIncomingGrainCallFilter(async context =>
{
    // Before grain call
    await context.Invoke();
    // After grain call
});

Для исходящих вызовов зерна от клиентов используйте AddOutgoingGrainCallFilter:

siloBuilder.AddOutgoingGrainCallFilter<MyOutgoingFilter>();
clientBuilder.AddOutgoingGrainCallFilter<MyOutgoingFilter>();

Критическое изменение: LeaseAquisitionPeriod исправлено опечатка

Неправильно написанное свойство LeaseAquisitionPeriod в LeaseBasedQueueBalancerOptions было исправлено на LeaseAcquisitionPeriod.

// Orleans 7.x (typo)
options.LeaseAquisitionPeriod = TimeSpan.FromSeconds(30);

// Orleans 10.0 (corrected)
options.LeaseAcquisitionPeriod = TimeSpan.FromSeconds(30);

Критическое изменение: LoadSheddingLimit переименовано в CpuThreshold

Свойство LoadSheddingLimit в LoadSheddingOptions было переименовано в CpuThreshold, чтобы лучше отражать его назначение.

// Orleans 7.x
siloBuilder.Configure<LoadSheddingOptions>(options =>
{
    options.LoadSheddingEnabled = true;
    options.LoadSheddingLimit = 95; // No longer works
});

// Orleans 10.0
siloBuilder.Configure<LoadSheddingOptions>(options =>
{
    options.LoadSheddingEnabled = true;
    options.CpuThreshold = 95; // Use this instead
});

Критическое изменение: CancelRequestOnTimeout по умолчанию изменено

Значение MessagingOptions.CancelRequestOnTimeout по умолчанию изменилось с true на false. Это означает, что Orleans по умолчанию больше не отправляет сообщение об отмене при истечении времени ожидания вызова зерна.

Если приложение зависит от предыдущего поведения, явно задайте этот параметр:

siloBuilder.Configure<SiloMessagingOptions>(options =>
{
    options.CancelRequestOnTimeout = true;
});

// For clients
clientBuilder.Configure<ClientMessagingOptions>(options =>
{
    options.CancelRequestOnTimeout = true;
});

Критическое изменение: поставщик ADO.NET требует Microsoft.Data.SqlClient

Теперь поставщики ADO.NET (кластеризация, сохраняемость, напоминания) требуют Microsoft.Data.SqlClient вместо System.Data.SqlClient. Обновите ссылки на проект:

<!-- Remove -->
<PackageReference Include="System.Data.SqlClient" Version="..." />

<!-- Add -->
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />

Инвариантное имя также изменилось:

// Orleans 7.x
options.Invariant = "System.Data.SqlClient";

// Orleans 10.0
options.Invariant = "Microsoft.Data.SqlClient";

Критическое изменение: [Unordered] устаревший атрибут

Атрибут [Unordered] для интерфейсов зерна теперь считается устаревшим и не оказывает никакого влияния. Порядок сообщений никогда не гарантируется независимо от этого атрибута. Удалите атрибут из кода:

// Orleans 7.x
[Unordered]
public interface IMyGrain : IGrainWithStringKey
{
    Task DoSomething();
}

// Orleans 10.0 - just remove the attribute
public interface IMyGrain : IGrainWithStringKey
{
    Task DoSomething();
}

Критическое изменение: OrleansConstructorAttribute устаревшее

Элемент OrleansConstructorAttribute устарел. Вместо этого используются типы GeneratedActivatorConstructorAttribute или ActivatorUtilitiesConstructorAttribute. Примените эти атрибуты только к конструкторам, которые требуют внедрения служб с помощью внедрения зависимостей. Не используйте их для указания того, как Orleans следует задать сериализованные свойства данных или поля.

public interface IMyDependency
{
}

// Orleans 7.x
[GenerateSerializer]
public class MyClass
{
    [Id(0)]
    public string Value { get; set; }

    [OrleansConstructor] // Obsolete and ignored
    public MyClass(IMyDependency dependency)
    {
        Dependency = dependency;
    }

    [field: NonSerialized]
    public IMyDependency Dependency { get; }
}

// Orleans 10.0
[GenerateSerializer]
public class MyClass
{
    [Id(0)]
    public string Value { get; set; }

    [GeneratedActivatorConstructor]
    public MyClass(IMyDependency dependency)
    {
        Dependency = dependency;
    }

    [field: NonSerialized]
    public IMyDependency Dependency { get; }
}

Критическое изменение: RegisterTimer устаревшее

Метод Grain.RegisterTimer устарел. Вместо этого используйте новые RegisterGrainTimer методы расширения, которые обеспечивают более эффективное управление поведением таймера.

// Orleans 7.x
public override Task OnActivateAsync(CancellationToken cancellationToken)
{
    RegisterTimer(
        callback: DoWork,
        state: null,
        dueTime: TimeSpan.FromSeconds(1),
        period: TimeSpan.FromSeconds(10));
    return Task.CompletedTask;
}

// Orleans 10.0
public override Task OnActivateAsync(CancellationToken cancellationToken)
{
    this.RegisterGrainTimer(
        callback: DoWork,
        state: (object?)null,
        options: new GrainTimerCreationOptions
        {
            DueTime = TimeSpan.FromSeconds(1),
            Period = TimeSpan.FromSeconds(10),
            Interleave = true // Set to true for same behavior as old RegisterTimer
        });
    return Task.CompletedTask;
}

Это важно

По умолчанию RegisterGrainTimer использует Interleave = false, что предотвращает перекрытие обратных вызовов таймера с другими вызовами грейнов. Если вам необходимо старое поведение, при котором обратные вызовы таймера могут выполняться параллельно, явно укажите Interleave = true.

Новые возможности в Orleans версии 10.0

После миграции вы можете воспользоваться этими новыми функциями:

Миграция с Orleans версии 8.0 на 9.0

При обновлении с Orleans версии 8.x обратите внимание на следующие дополнительные изменения, представленные в Orleans версии 9.0:

  • Каталог с строгой согласованностью: каталог зерна по умолчанию теперь обеспечивает более строгие гарантии согласованности.
  • Полная поддержка CancellationToken: методы Grain теперь полностью поддерживают параметры CancellationToken
  • Активация на основе памяти: автоматическая деактивация под давлением памяти
  • Более быстрый протокол членства: время обнаружения сбоев по умолчанию сокращается с 10 минут до 90 секунд
  • Размещение по умолчанию изменилось на ResourceOptimized (9.2+): стратегия размещения зерна по умолчанию была изменена с RandomPlacement на ResourceOptimizedPlacement.

Если приложение использует случайное размещение, явно настройте его:

siloBuilder.Services.AddSingleton<PlacementStrategy, RandomPlacement>();

// Or on specific grains
[RandomPlacement]
public class MyGrain : Grain, IMyGrain { }

Миграция с Orleans 7.0 на 8.0

При обновлении с Orleans версии 7.x обратите внимание на эти изменения, представленные в Orleans версии 8.0:

  • Новый API таймера: RegisterGrainTimer был введен для замены RegisterTimer
  • .NET Aspire integration: первоклассная поддержка .NET Aspire
  • Оптимизированное по ресурсам размещение: новая стратегия размещения на основе использования процессора и оперативной памяти
  • Репартиция активации (8.2+): экспериментальная функция автоматического перебалансирования элементов

Последовательные обновления

Последовательное обновление с Orleans версии 7.x до 10.0 не рекомендуется из-за значительных изменений протокола и API. Вместо этого:

  1. Развертывание нового кластера под управлением Orleans 10.0
  2. Перенос данных о состоянии при необходимости
  3. Переключение трафика на новый кластер
  4. Вывод из эксплуатации старого кластера

Скрипты миграции ADO.NET

Если вы используете ADO.NET для кластеризации, сохраняемости или напоминаний, примените соответствующие сценарии миграции:

Переход с Orleans версии 3.x на 7.0

Orleans 7.0 представляет несколько полезных изменений, в том числе улучшения хостинга, пользовательской сериализации, неизменяемости и абстракции зерновых единиц.

Миграция

Из-за изменений в том, как Orleans идентифицирует зерна и потоки, перенос существующих приложений, использующих напоминания, потоки или сохраняемость зерна в Orleans 7.0 в текущий момент является непростой задачей.

Плавное обновление приложений, работающих в предыдущих Orleans версиях с помощью последовательного обновления до Orleans версии 7.0, невозможно. Поэтому используйте другую стратегию обновления, например развертывание нового кластера и вывод из эксплуатации предыдущего. Orleans 7.0 изменяет протокол проводной связи несовместимо, что означает, что кластеры не могут содержать сочетание Orleans узлов 7.0 и узлов, работающих с предыдущими Orleans версиями.

Таких кардинальных изменений избегали в течение многих лет, даже в крупных релизах. Почему сейчас? Существует две основные причины: идентификаторы и сериализация. Что касается идентификаторов, зерновые и потоковые идентификаторы теперь являются строками. Это позволяет правильно кодировать информацию об универсальных типах и упрощает сопоставление потоков с контекстом приложения. Ранее Orleans идентифицировал типы зерна, используя сложную структуру данных, которая не могла представлять универсальные типы зерна, что приводило к особым случаям. Потоки были определены string пространством имен и Guid ключом, которые были эффективны, но трудны для сопоставления с доменом приложения. В процессе сериализации теперь поддерживается совместимость между версиями. Это означает, что типы могут быть изменены определенными совместимыми способами, следуя набору правил, с уверенностью в том, что приложение можно обновить без ошибок сериализации. Эта возможность особенно полезна, если типы приложений сохраняются в потоках или хранилище зерна. В следующих разделах подробно описаны основные изменения и их дальнейшие обсуждения.

Изменения упаковки

При обновлении проекта до Orleans версии 7.0 выполните следующие действия:

  • Все клиенты должны ссылаться на Microsoft.Orleans. Клиент.
  • Все силосы (серверы) должны ссылаться на Microsoft.Orleans. Server.
  • Все остальные пакеты должны ссылаться на Microsoft.Orleans.Sdk.
  • Удалите все ссылки на Microsoft.Orleans.CodeGenerator.MSBuild и Microsoft.Orleans.OrleansCodeGenerator.Build.
    • Замените использование KnownAssembly на GenerateCodeForDeclaringAssemblyAttribute.
    • Пакет Microsoft.Orleans.Sdk ссылается на пакет генератора источников C# (Microsoft.Orleans.CodeGenerator).
  • Удалите все ссылки на Microsoft.Orleans.OrleansRuntime.
  • Удалите вызовы ConfigureApplicationParts. Части приложения удалены. Генератор Orleans источника C# добавляется ко всем пакетам (включая клиент и сервер) и автоматически создает эквивалент частей приложения.
  • Замените ссылки на Microsoft.Orleans.OrleansServiceBus на Microsoft.Orleans.Streaming.EventHubs.
  • При использовании напоминаний добавьте ссылку на Microsoft.Orleans.Reminders.
  • При использовании потоков добавьте ссылку на Orleans.

Совет

Orleans Все примеры обновлены до Orleans версии 7.0 и могут использоваться в качестве ссылки на внесенные изменения. Дополнительные сведения см. в статье Orleans #8035 , которая содержит изменения, внесенные в каждый пример.

Orleans глобальные директивы `using`

Все проекты Orleans напрямую или косвенно ссылались на пакет NuGet Microsoft.Orleans.Sdk. Orleans Если проект настроен для включения неявных подключений (например, <ImplicitUsings>enable</ImplicitUsings>), проект неявно подключает оба пространства имен: Orleans и Orleans.Hosting. Это означает, что код приложения не нуждается в этих using директивах.

Дополнительные сведения см. в разделе ImplicitUsings и dotnet/orleans/src/Orleans. Sdk/build/Microsoft.Orleans. Sdk.targets.

Хостинг

Тип ClientBuilder заменяется методом расширения UseOrleansClient на IHostBuilder. Тип IHostBuilder приходит из пакета NuGet Microsoft.Extensions.Hosting. Это означает, что клиент Orleans может быть добавлен в существующий хост без создания отдельного контейнера для внедрения зависимостей. Клиент подключается к кластеру во время запуска. После завершения IHost.StartAsync клиент подключается автоматически. Службы, добавленные в IHostBuilder, запускаются в порядке регистрации. Вызов UseOrleansClient перед вызовом ConfigureWebHostDefaults, например гарантирует, что Orleans запускается перед запуском ASP.NET Core, что позволяет немедленно получить доступ к клиенту из приложения ASP.NET Core.

Чтобы эмулировать предыдущее ClientBuilder поведение, создайте отдельный HostBuilderOrleans объект и настройте его с помощью клиента. Можно настроить IHostBuilder с помощью клиента Orleans или силоса Orleans. Все силосы регистрируют экземпляр IGrainFactory и IClusterClient, которые приложение может использовать, поэтому настройка клиента отдельно не требуется и не поддерживается.

OnActivateAsync изменение подписи и OnDeactivateAsync изменение подписи

Orleans позволяет зернам выполнять код во время активации и деактивации. Используйте эту возможность для выполнения таких задач, как чтение состояния из хранилища или ведение журнала сообщений жизненного цикла. В Orleans версии 7.0 сигнатура этих методов жизненного цикла изменилась:

  • OnActivateAsync() теперь принимает CancellationToken параметр. При отмене CancellationToken прекратите процесс активации.
  • OnDeactivateAsync() теперь принимает DeactivationReason параметр и CancellationToken параметр. DeactivationReason показывает, почему активация деактивируется. Используйте эти сведения для ведения журнала и диагностики. После отмены CancellationToken завершите процесс деактивации немедленно. Обратите внимание, что поскольку любой узел может выйти из строя в любое время, не рекомендуется доверять OnDeactivateAsync для выполнения важных действий, таких как сохранение критического состояния.

Рассмотрим следующий пример переопределения этих новых методов:

public sealed class PingGrain : Grain, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(ILogger<PingGrain> logger) =>
        _logger = logger;

    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public override Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Зерна POCO и IGrainBase

Компоненты в Orleans больше не обязаны наследовать от базового класса Grain или любого другого класса. Эта функция называется зернами POCO . Для доступа к методам расширения, например таким, как следующие:

Зерно должно реализовывать IGrainBase или наследовать от Grain. Ниже приведен пример реализации IGrainBase для класса grain:

public sealed class PingGrain : IGrainBase, IPingGrain
{
    public PingGrain(IGrainContext context) => GrainContext = context;

    public IGrainContext GrainContext { get; }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

IGrainBase также определяет OnActivateAsync и OnDeactivateAsync с реализациями по умолчанию, что позволяет зерну при необходимости участвовать в его жизненном цикле.

public sealed class PingGrain : IGrainBase, IPingGrain
{
    private readonly ILogger<PingGrain> _logger;

    public PingGrain(IGrainContext context, ILogger<PingGrain> logger)
    {
        _logger = logger;
        GrainContext = context;
    }

    public IGrainContext GrainContext { get; }

    public Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnActivateAsync()");
        return Task.CompletedTask;
    }

    public Task OnDeactivateAsync(DeactivationReason reason, CancellationToken token)
    {
        _logger.LogInformation("OnDeactivateAsync({Reason})", reason);
        return Task.CompletedTask;
    }

    public ValueTask Ping() => ValueTask.CompletedTask;
}

Сериализация

Самым обременительным изменением в Orleans версии 7.0 является введение сериализатора с поддержкой различных версий. Это изменение было сделано из-за того, что приложения, как правило, развиваются, что привело к значительной ловушке для разработчиков, так как предыдущий сериализатор не мог терпеть добавление свойств в существующие типы. С другой стороны, предыдущий сериализатор был гибким, позволяя представлять большинство .NET типов без изменений, включая такие функции, как универсальные, полиморфизм и отслеживание ссылок. Замена была давно просроченной, но представление типов с высоким уровнем точности по-прежнему необходимо. Таким образом, Orleans 7.0 представляет заменяющий сериализатор, обеспечивающий высокую точность представления типов .NET и позволяющий типам развиваться. Новый сериализатор значительно эффективнее предыдущего, что обеспечивает повышение сквозной пропускной способности до 170%.

Дополнительные сведения см. в следующих статьях, связанных с Orleans 7.0:

Идентификаторы зерна

У каждого зерна есть уникальная идентичность, состоящая из типа зерна и его ключа. Предыдущие Orleans версии использовали составной тип для GrainId, чтобы поддерживать ключи зерна одного из следующих типов:

Этот подход включает в себя определенные сложности при работе с ключами данных. Идентификаторы зерна состоят из двух компонентов: типа и ключа. Компонент типа ранее состоял из числового кода типа, категории и 3 байта сведений универсального типа.

Идентификаторы зерна теперь принимают форму type/key, где и type, и key являются строками. Наиболее часто используемый интерфейс зернового ключа — это IGrainWithStringKey. Это значительно упрощает работу идентификации зерна и улучшает поддержку обобщённых типов зерна.

Интерфейсы зерновых элементов теперь также представлены с помощью удобочитаемого имени, а не сочетания хэш-кода и строкового представления любых параметров универсального типа.

Новая система является более настраиваемой, и эти настройки можно управлять атрибутами.

  • GrainTypeAttribute(String) в зерне class указывает часть типа его идентификатора зерна.
  • DefaultGrainTypeAttribute(String)указываемый в зерне interface зерна, который должен определяться по умолчаниюIGrainFactory при получении ссылки на зерно. Например, при вызове IGrainFactory.GetGrain<IMyGrain>("my-key")фабрика зерна возвращает ссылку на зерно "my-type/my-key" , если IMyGrain указан указанный выше атрибут.
  • GrainInterfaceTypeAttribute(String) позволяет переопределить имя интерфейса. Указание имени явно с помощью этого механизма позволяет переименовать тип интерфейса без нарушения совместимости с существующими ссылками на объекты. Обратите внимание, что интерфейс также должен иметь AliasAttribute в этом случае, так как его идентификация может быть сериализована. Дополнительные сведения об указании псевдонима типа см. в разделе о сериализации.

Как упоминалось выше, переопределение имен класса и имен интерфейсов по умолчанию для типов позволяет переименовать базовые типы без нарушения совместимости с существующими развертываниями.

Поток идентификаторов

Когда Orleans потоки были впервые выпущены, их можно было определить только с помощью Guid. Этот подход был эффективным с точки зрения выделения памяти, но при создании значимых идентификаторов потоков часто требовало кодирования или использования косвенных методов для определения соответствующего идентификатора потока для заданной цели.

В Orleans версии 7.0 потоки определяются с помощью строк. Содержит Orleans.Runtime.StreamIdstruct три свойства: StreamId.Namespace, StreamId.Keyи StreamId.FullKey. Эти значения свойств кодируются строками UTF-8. Например, ознакомьтесь со статьей StreamId.Create(String, String).

Замена SimpleMessageStreams на BroadcastChannel

SimpleMessageStreams (также называемый SMS) удаляется в версии 7.0. SMS имел тот же интерфейс, что Orleans.Providers.Streams.PersistentStreams, но его поведение значительно отличалось, потому что он опирался на прямые вызовы между зернами. Чтобы избежать путаницы, SMS было удалено, и была введена новая замена, названная Orleans.BroadcastChannel.

BroadcastChannel поддерживает только неявные подписки и может служить прямой заменой в этом случае. Если необходимы явные подписки или PersistentStream интерфейс должен использоваться (например, если SMS использовался в тестах, а EventHub в рабочей среде), то MemoryStream является лучшим кандидатом.

BroadcastChannel имеет то же поведение, что и SMS, а MemoryStream ведет себя подобно другим поставщикам потоков. Рассмотрим следующий пример использования широковещательного канала:

// Configuration
builder.AddBroadcastChannel(
    "my-provider",
    options => options.FireAndForgetDelivery = false);

// Publishing
var grainKey = Guid.NewGuid().ToString("N");
var channelId = ChannelId.Create("some-namespace", grainKey);
var stream = provider.GetChannelWriter<int>(channelId);

await stream.Publish(1);
await stream.Publish(2);
await stream.Publish(3);

// Simple implicit subscriber example
[ImplicitChannelSubscription]
public sealed class SimpleSubscriberGrain : Grain, ISubscriberGrain, IOnBroadcastChannelSubscribed
{
    // Called when a subscription is added to the grain
    public Task OnSubscribed(IBroadcastChannelSubscription streamSubscription)
    {
        streamSubscription.Attach<int>(
          item => OnPublished(streamSubscription.ChannelId, item),
          ex => OnError(streamSubscription.ChannelId, ex));

        return Task.CompletedTask;

        // Called when an item is published to the channel
        static Task OnPublished(ChannelId id, int item)
        {
            // Do something
            return Task.CompletedTask;
        }

        // Called when an error occurs
        static Task OnError(ChannelId id, Exception ex)
        {
            // Do something
            return Task.CompletedTask;
        }
    }
}

Миграция на MemoryStream становится проще, так как требуется изменить только конфигурацию. Рассмотрим следующую MemoryStream конфигурацию:

builder.AddMemoryStreams<DefaultMemoryMessageBodySerializer>(
    "in-mem-provider",
    _ =>
    {
        // Number of pulling agent to start.
        // DO NOT CHANGE this value once deployed, if you do rolling deployment
        _.ConfigurePartitioning(partitionCount: 8);
    });

OpenTelemetry

Система телеметрии обновляется в Orleans 7.0, а предыдущая система удаляется в пользу стандартных API-интерфейсов .NET, таких как .NET Metrics для метрик и ActivitySource для отслеживания.

В рамках этого удаляются существующие пакеты Microsoft.Orleans.TelemetryConsumers.*. Рассматривается новый набор пакетов для упрощения интеграции метрик, выводимых Orleans, в решение мониторинга по выбору. Как всегда, отзывы и вклады приветствуются.

Инструмент dotnet-counters предоставляет возможности мониторинга производительности для оперативной оценки состояния системы и исследования основных показателей производительности. Для Orleans счетчиков используйте средство dotnet-counters для их мониторинга:

dotnet counters monitor -n MyApp --counters Microsoft.Orleans

Аналогичным образом добавьте метрики Microsoft.Orleans в метрики OpenTelemetry, как показано в следующем коде:

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics => metrics
        .AddPrometheusExporter()
        .AddMeter("Microsoft.Orleans"));

Чтобы включить распределенную трассировку, настройте OpenTelemetry, как показано в следующем коде:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.SetResourceBuilder(ResourceBuilder.CreateDefault()
            .AddService(serviceName: "ExampleService", serviceVersion: "1.0"));

        tracing.AddAspNetCoreInstrumentation();

        // Good baseline for general Orleans observability
        tracing.AddSource(Orleans.Diagnostics.ActivitySources.ApplicationGrainActivitySourceName);
        tracing.AddSource(Orleans.Diagnostics.ActivitySources.LifecycleActivitySourceName);

        /*
        // Other source also available
        // Persistence spans
        tracing.AddSource(Orleans.Diagnostics.ActivitySources.StorageActivitySourceName);
        // Internal Runtime spans
        tracing.AddSource(Orleans.Diagnostics.ActivitySources.RuntimeActivitySourceName);
        */

        /*
        // Optionally add all Microsoft.Orleans.* Sources at once
        tracing.AddSource(Orleans.Diagnostics.ActivitySources.AllActivitySourceName);
        */

        tracing.AddZipkinExporter(options =>
        {
            options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
        });
    });

В приведенном выше коде openTelemetry настроен для мониторинга:

  • Microsoft.Orleans.Application
  • Microsoft.Orleans.Lifecycle

Чтобы распространить действие, вызовите AddActivityPropagation:

builder.Host.UseOrleans((_, clientBuilder) =>
{
    clientBuilder.AddActivityPropagation();
});

Рефакторинг функций из основного пакета в отдельные пакеты

В версии Orleans 7.0 расширения были выделены в отдельные пакеты, которые не зависят от Orleans.Core. А именно, Orleans.Streamingи Orleans.RemindersOrleans.Transactions были отделены от ядра. Это означает, что эти пакеты полностью платят за то, что используется, и код в ядре Orleans не предназначен для этих функций. Этот подход сокращает размер основной поверхности API и размера сборки, упрощает ядро и повышает производительность. В отношении производительности транзакции в Orleans ранее требовали некоторого кода, выполняемого для каждого метода для координации потенциальных транзакций. Логика координации теперь перенесена на уровень каждого метода.

Это изменение, нарушающее компиляцию. Существующий код, взаимодействующий с напоминаниями или потоками путем вызова методов, ранее определенных в Grain базовом классе, может нарушиться, так как теперь это методы расширения. Обновите такие вызовы, которые не уточняют this (например, GetReminders), чтобы включить this (например, this.GetReminders()), поскольку методы расширения должны быть квалифицированы. Ошибка компиляции возникает, если эти вызовы не обновляются, и обязательное изменение кода может не быть очевидным, не зная, что изменилось.

Клиент транзакции

Orleans7.0 представляет новую абстракцию для координации транзакций: Orleans.ITransactionClient Ранее только зерна могли координировать транзакции. С помощью внедрения зависимостей через ITransactionClient, клиенты также могут координировать транзакции без необходимости промежуточного грейна. Следующий пример выводит кредиты из одной учетной записи и помещает их в другую в рамках одной транзакции. Вызовите этот код из грейна или внешнего клиента, который получил ITransactionClient из контейнера внедрения зависимостей.

await transactionClient.RunTransaction(
  TransactionOption.Create,
  () => Task.WhenAll(from.Withdraw(100), to.Deposit(100)));

Для транзакций, координируемых клиентом, клиент должен добавить необходимые службы во время настройки:

clientBuilder.UseTransactions();

Пример BankAccount демонстрирует использование ITransactionClient. Дополнительные сведения см. в разделе Orleans "Транзакции".

Повторное получение цепочки вызовов

Грейны являются однопоточными и обрабатывают запросы последовательно от начала до конца по умолчанию. Иными словами, зерна не являются реэнтерабельными по умолчанию. Добавление ReentrantAttribute к классу зерна позволяет зерну обрабатывать несколько запросов одновременно в чередующемся порядке, оставаясь однопоточным. Эта возможность может быть полезна для зерн, не имеющих внутреннего состояния или выполнения многих асинхронных операций, таких как выдача http-вызовов или запись в базу данных. Требуется повышенное внимание, когда запросы могут пересечься: возможно, что состояние зерна, наблюдаемое до изменения оператора await, меняется к моменту завершения асинхронной операции, и метод возобновляет выполнение.

Например, следующее зерно представляет счетчик. Она помечена ReentrantAttribute, что позволяет выполнять несколько вызовов для взаимодействия. Метод Increment() должен увеличивать внутренний счетчик и возвращать наблюдаемое значение. Тем не менее, поскольку тело метода Increment() наблюдает состояние зерна до точки await и обновляет его после этого, несколько чередующихся выполнений Increment() может привести к _value, меньшему, чем общее количество полученных вызовов Increment(). Это ошибка из-за неправильного использования реентерабельности.

Удаление ReentrantAttribute достаточно, чтобы устранить эту проблему.

[Reentrant]
public sealed class CounterGrain : Grain, ICounterGrain
{
    int _value;

    /// <summary>
    /// Increments the grain's value and returns the previous value.
    /// </summary>
    public Task<int> Increment()
    {
        // Do not copy this code, it contains an error.
        var currentVal = _value;
        await Task.Delay(TimeSpan.FromMilliseconds(1_000));
        _value = currentVal + 1;
        return currentValue;
    }
}

Чтобы предотвратить такие ошибки, по умолчанию grains не имеют возможности повторного входа. Недостатком является снижение пропускной способности для зерен, выполняющих асинхронные операции в своей реализации, поскольку зерна не могут обрабатывать другие запросы, пока ожидается завершение асинхронной операции. Чтобы устранить эту проблему, Orleans предлагает несколько вариантов, чтобы разрешить повторный вход в некоторых случаях.

  • Для всего класса: размещение ReentrantAttribute на элемент позволяет любому запросу к элементу чередоваться с любым другим запросом.
  • Для подмножества методов: размещение AlwaysInterleaveAttribute метода интерфейса зерна позволяет запросам к этому методу взаимодействовать с любым другим запросом и позволяет любому другому запросу пересекать запросы к этому методу.
  • Для подмножества методов: размещение ReadOnlyAttribute метода на зерне интерфейса позволяет запросам к этому методу взаимодействовать с любым другим запросом и позволяет любому другому запросу ReadOnlyAttribute взаимодействовать с запросами к этому методу. В этом смысле это более ограниченная форма AlwaysInterleaveAttribute.
  • Для любого запроса в цепочке вызовов: RequestContext.AllowCallChainReentrancy() и RequestContext.SuppressCallChainReentrancy() позволяют включать и отключать подчиненные запросы для повторного вызова зерна. Оба вызова возвращают значение, которое должно быть удалено при выходе из запроса. Поэтому используйте их следующим образом:
public Task<int> OuterCall(IMyGrain other)
{
    // Allow call-chain reentrancy for this grain, for the duration of the method.
    using var _ = RequestContext.AllowCallChainReentrancy();
    await other.CallMeBack(this.AsReference<IMyGrain>());
}

public Task CallMeBack(IMyGrain grain)
{
    // Because OuterCall allowed reentrancy back into that grain, this method
    // will be able to call grain.InnerCall() without deadlocking.
    await grain.InnerCall();
}

public Task InnerCall() => Task.CompletedTask;

Включите повторный вход в цепочки вызовов для каждого зерна, для каждой цепочки вызовов. Например, рассмотрим два зерна, A и B. Если зерно A позволяет повторный вход в цепочку вызовов перед вызовом зерна B, зерно B может вернуться в зерно A в рамках этого вызова. Тем не менее, зерно A не может вернуться в зерно B, если зерно B также не включило повторную цепочку вызовов. Она включена для каждого зерна, для каждой цепочки вызовов.

Зерна также могут подавлять передачу сведений о повторном входе в цепочке вызовов, используя using var _ = RequestContext.SuppressCallChainReentrancy(). Это предотвращает повторение последующих вызовов.

Скрипты миграции ADO.NET

Чтобы обеспечить совместимость с кластеризацией, устойчивостью и напоминаниями, основанными на ADO.NET, требуется соответствующий скрипт миграции SQL.

Выберите файлы для используемой базы данных и примените их по порядку.

Переход с Orleans версии 3.x на 7.0

Для пользователей версии Orleans 3.x следуйте руководству по миграции, находящемуся в разделе документации Orleans 7.0, используя селектор версий выше.

Это важно

Orleans 3.x больше не поддерживается. Рассмотрите возможность миграции на Orleans 10.0 для последних функций и обновлений системы безопасности.