Поделиться через


Сохранение детализации

С гранями может быть связано несколько именованных постоянных объектов данных. Эти объекты состояния загружаются из хранилища во время активации детализации, чтобы они были доступны во время запросов. В сохраняемости детализации используется расширяемая модель подключаемого модуля, чтобы можно было использовать поставщики хранилища для любой базы данных. Эта модель сохраняемости предназначена для простоты и не предназначена для покрытия всех шаблонов доступа к данным. Грани также могут напрямую обращаться к базам данных без использования модели сохраняемости граней.

На приведенной выше схеме Усерграин имеет состояние профиля и состояние корзины , каждое из которых хранится в отдельной системе хранения.

Цели

  1. Несколько именованных постоянных объектов данных на детализацию.
  2. Несколько настроенных поставщиков хранилища, каждый из которых может иметь другую конфигурацию и поддерживаться другой системой хранения.
  3. поставщики служба хранилища могут быть разработаны и опубликованы сообществом.
  4. поставщики служба хранилища имеют полный контроль над тем, как они хранят детализированные данные о состоянии в постоянном резервном хранилище. Кообъект: Orleans не предоставляет исчерпывающее решение для хранения ORM, но вместо этого позволяет поставщикам пользовательских хранилищ поддерживать конкретные требования ORM, как и при необходимости.

Пакеты

Поставщики хранилища Orleansности можно найти на NuGet. Официально обслуживаемые пакеты включают:

API

Грани взаимодействуют с постоянным состоянием, используя IPersistentState<TState> , где TState — сериализуемый тип состояния:

public interface IPersistentState<TState> where TState : new()
{
    TState State { get; set; }
    string Etag { get; }
    Task ClearStateAsync();
    Task WriteStateAsync();
    Task ReadStateAsync();
}

IPersistentState<TState>Экземпляры вставляются в степень детализации как параметры конструктора. Эти параметры можно снабдить PersistentStateAttribute атрибутом, чтобы указать имя вставляемого состояния и имя поставщика хранилища, который его предоставляет. В следующем примере показано, как внедрить два именованных состояния в UserGrain конструктор:

public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;
    private readonly IPersistentState<CartState> _cart;

    public UserGrain(
        [PersistentState("profile", "profileStore")] IPersistentState<ProfileState> profile,
        [PersistentState("cart", "cartStore")] IPersistentState<CartState> cart)
    {
        _profile = profile;
        _cart = cart;
    }
}

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

Состояние чтения

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

Если нужно явно повторно прочитать Последнее состояние для этого уровня из резервного хранилища, то детализация должна вызвать ReadStateAsync метод. Это приведет к перезагрузке состояния детализации из постоянного хранилища с помощью поставщика хранилища, а предыдущая копия состояния детализации в памяти будет перезаписана и заменена TaskReadStateAsync() по завершении.

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

public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

Не нужно вызывать ReadStateAsync() во время нормальной работы; состояние загружается автоматически во время активации. ReadStateAsync()Однако может использоваться для обновления состояния, которое изменяется извне.

Дополнительные сведения о механизмах обработки ошибок см. в разделе режимы сбоя ниже.

Состояние записи

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

public async Task SetNameAsync(string name)
{
    _profile.State.Name = name;
    await _profile.WriteStateAsync();
}

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

Дополнительные сведения о механизмах обработки ошибок см. в разделе режимы сбоя ниже.

Очистить состояние

ClearStateAsyncМетод очищает состояние детализации в хранилище. В зависимости от поставщика эта операция может при необходимости полностью удалить состояние детализации.

Приступая к работе

Прежде чем можно будет использовать сохраняемость, на приемнике данных должен быть настроен поставщик хранилища.

Сначала настройте поставщики хранилища, один для состояния профиля и один для состояния корзины:

var host = new HostBuilder()
    .UseOrleans(siloBuilder =>
    {
        siloBuilder.AddAzureTableGrainStorage(
            name: "profileStore",
            configureOptions: options =>
            {
                // Use JSON for serializing the state in storage
                options.UseJson = true;

                // Configure the storage connection key
                options.ConnectionString =
                    "DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1";
            })
            .AddAzureBlobGrainStorage(
                name: "cartStore",
                configureOptions: options =>
                {
                    // Use JSON for serializing the state in storage
                    options.UseJson = true;

                    // Configure the storage connection key
                    options.ConnectionString =
                        "DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2";
                });
    })
    .Build();

Теперь, когда поставщик хранилища настроен с именем "profileStore" , у него есть возможность получить доступ к этому поставщику.

Постоянное состояние можно добавить в степень детализации двумя основными способами.

  1. Путем внедрения IPersistentState<TState> в конструктор граней.
  2. Путем наследования от Grain<TGrainState> .

Рекомендуемый способ добавления хранилища в степень детализации — внедрение IPersistentState<TState> в конструктор граней со связанным [PersistentState("stateName", "providerName")] атрибутом. Дополнительные сведения о см. ниже. Это по-прежнему поддерживается, но считается устаревшим подходом.

Объявите класс для хранения состояния детализации:

[Serializable]
public class ProfileState
{
    public string Name { get; set; }

    public Date DateOfBirth
}

Внедрение IPersistentState<ProfileState> в конструктор граней:

public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrain(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }
}

Примечание

Состояние профиля не будет загружено в момент, когда оно будет вставлено в конструктор, поэтому доступ к нему в это время недопустим. Состояние будет загружено перед OnActivateAsync вызовом метода.

Теперь, когда степень детализации находится в постоянном состоянии, мы можем добавить методы для чтения и записи состояния:

public class UserGrain : Grain, IUserGrain
    {
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrain(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }

    public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

    public async Task SetNameAsync(string name)
    {
        _profile.State.Name = name;
        await _profile.WriteStateAsync();
    }
}

Режимы сбоя для операций сохранения состояния

Режимы сбоя для операций чтения

Ошибки, возвращенные поставщиком хранилища во время первоначального считывания данных о состоянии для этого конкретного фрагмента, не будут завершать операцию активации для этого детализации. в этом случае вызов метода обратного вызова жизненного цикла не будет вызываться . Исходный запрос к степени детализации, вызвавшей активацию, будет возвращен вызывающему объекту так же, как и любой другой сбой при активации детализации. Ошибки, обнаруженные поставщиком хранилища при чтении данных о состоянии для определенного детализации, приводят к исключению из ReadStateAsync()Task . Детализация может обрабатывать или игнорировать Task исключение, как и любые другие Task в Orleans.

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

Режимы сбоя для операций записи

Ошибки, обнаруженные поставщиком хранилища при записи данных о состоянии для определенного детализации, приводят к возникновению исключения WriteStateAsync()Task . Как правило, это означает, что исключение "гранное обращение" будет создано обратно вызывающему клиенту. при условии, что объект WriteStateAsync()Task правильно связан с окончательным возвратом Task для этого метода детализации. Однако в некоторых расширенных сценариях можно написать код детализации, чтобы специально обрабатывал такие ошибки записи, так же, как они могут справиться с другими Task ошибками.

Грани, которые выполняют код обработки ошибок или восстановления, должны перехватывать исключения или ошибки Task , а также не создавать их повторно, чтобы показать, что они успешно обрабатывали ошибку записи.

Рекомендации

Использовать сериализацию JSON или другой формат сериализации с поддержкой версий

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

Использование граней < TState > для добавления хранилища в степень детализации

Важно!

Использование Grain<T> для добавления хранилища в степень считается Grain<T> функциональностью. объемное хранилище следует добавлять с помощью IPersistentState<T> , как описано выше.

Многогранные классы, которые наследуют от Grain<T> (где T является типом данных состояния для конкретного приложения, который необходимо сохранить), будут автоматически загружаться из указанного хранилища.

Такие Гранки отмечены StorageProviderAttribute атрибутом, который указывает именованный экземпляр поставщика хранилища, используемый для чтения и записи данных о состоянии для этого детализации.

[StorageProvider(ProviderName="store1")]
public class MyGrain : Grain<MyGrainState>, /*...*/
{
  /*...*/
}

Grain<T>Базовый класс определил следующие методы для вызова подклассов:

protected virtual Task ReadStateAsync() { /*...*/ }
protected virtual Task WriteStateAsync() { /*...*/ }
protected virtual Task ClearStateAsync() { /*...*/ }

Поведение этих методов соответствует их аналогам IPersistentState<TState> , определенным ранее.

Создание поставщика хранилища

Существуют две части API сохраняемости состояния: API, доступный для граней через IPersistentState<T> или Grain<T> , и API поставщика хранилища, который выравнивается по центру IGrainStorage — интерфейсу, который должны реализовывать поставщики хранилища:

/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface IGrainStorage
{
    /// <summary>Read data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be populated for this grain.</param>
    /// <returns>Completion promise for the Read operation on the specified grain.</returns>
    Task ReadStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Write data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be written for this grain.</param>
    /// <returns>Completion promise for the Write operation on the specified grain.</returns>
    Task WriteStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Delete / Clear data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">Copy of last-known state data object for this grain.</param>
    /// <returns>Completion promise for the Delete operation on the specified grain.</returns>
    Task ClearStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);
}

Создайте пользовательский поставщик хранилища, реализовав этот интерфейс и зарегистрировав эту реализацию. Пример существующей реализации поставщика хранилища см. в разделе AzureBlobGrainStorage .

семантика поставщика служба хранилища

Значение непрозрачного поставщика Etag ( string ) Etag быть задано поставщиком хранилища как часть метаданных состояния детализации, заполняемых при считывании состояния. Некоторые поставщики могут оставить это, null если они не используют Etag s.

Любая попытка выполнить операцию записи, когда поставщик хранилища обнаруживает Etag нарушение ограничения, Etag привести к ошибке записи Task с временной ошибкой InconsistentStateException и заключением исключения базового хранилища.

public class InconsistentStateException : OrleansException
{
    public InconsistentStateException(
    string message,
    string storedEtag,
    string currentEtag,
    Exception storageException)
        : base(message, storageException)
    {
        StoredEtag = storedEtag;
        CurrentEtag = currentEtag;
    }

    public InconsistentStateException(
        string storedEtag,
        string currentEtag,
        Exception storageException)
        : this(storageException.Message, storedEtag, currentEtag, storageException)
    {
    }

    /// <summary>The Etag value currently held in persistent storage.</summary>
    public string StoredEtag { get; }

    /// <summary>The Etag value currently held in memory, and attempting to be updated.</summary>
    public string CurrentEtag { get; }
}

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

Сопоставление данных

Отдельные поставщики хранилища должны решить, как лучше хранить состояние зернистости — большой двоичный объект (различные форматы и сериализованные формы) или поле «столбец на» является очевидным выбором.

Регистрация поставщика хранилища

Среда выполнения Orleans будет разрешать поставщик хранилища от поставщика услуг ( IServiceProvider ) при создании детализации. Среда выполнения будет разрешать экземпляр IGrainStorage . Если поставщик хранилища имеет имя, например с помощью [PersistentState(stateName, storageName)] атрибута, то будет разрешен именованный экземпляр IGrainStorage .

Чтобы зарегистрировать именованный экземпляр IGrainStorage , используйте AddSingletonNamedService метод расширения, следующий за примером IGrainStorage.