Прочитать на английском

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


Разработка детализации

перед написанием кода для реализации класса граней создайте новый проект библиотеки классов, предназначенный для .NET Standard или .Net Core (предпочтительно) или платформа .NET Framework 4.6.1 или более поздней версии (если вы не можете использовать .NET Standard или .net Core из-за зависимостей). Многогранные интерфейсы и классы граней могут быть определены в одном проекте библиотеки классов или в двух разных проектах для лучшего разделения интерфейсов от реализации. в любом случае проекты должны ссылаться на microsoft. Orleans. Core. абстракции и microsoft. Orleans. CodeGenerator. MSBuild NuGet пакетах.

более подробные инструкции см. в разделе Project Setupучебника 1 – Orleans основы.

Детализация интерфейсов и классов

Грани взаимодействуют друг с другом и вызываются извне, вызывая методы, объявленные как часть соответствующих интерфейсов детализации. Класс граней реализует один или несколько ранее объявленных интерфейсов граней. Все методы многогранного интерфейса должны возвращать Task (для void методов), или ValueTask<TResult> (для методов, Task<TResult> возвращающих значения типа T ).

Ниже приведен фрагмент кода из примера службы присутствия Orleans версии 1,5:

public interface IPlayerGrain : IGrainWithGuidKey
{
    Task<IGameGrain> GetCurrentGame();
    Task JoinGame(IGameGrain game);
    Task LeaveGame(IGameGrain game);
}

public class PlayerGrain : Grain, IPlayerGrain
{
    private IGameGrain _currentGame;

    // Game the player is currently in. May be null.
    public Task<IGameGrain> GetCurrentGame()
    {
       return Task.FromResult(_currentGame);
    }

    // Game grain calls this method to notify that the player has joined the game.
    public Task JoinGame(IGameGrain game)
    {
       _currentGame = game;

       Console.WriteLine(
           $"Player {GetPrimaryKey()} joined game {game.GetPrimaryKey()}");

       return Task.CompletedTask;
    }

   // Game grain calls this method to notify that the player has left the game.
   public Task LeaveGame(IGameGrain game)
   {
       _currentGame = null;

       Console.WriteLine(
           $"Player {GetPrimaryKey()} left game {game.GetPrimaryKey()}");

       return Task.CompletedTask;
   }
}

Возвращаемые значения из методов детализации

Метод детализации, возвращающий значение типа T , определяется в интерфейсе с детализацией в виде возвращаемого Task<T> значения. Для методов детализации, не помеченных async ключевым словом, если возвращаемое значение доступно, оно обычно возвращается с помощью следующей инструкции:

public Task<SomeType> GrainMethod1()
{
    return Task.FromResult(GetSomeType());
}

Метод детализации, который не возвращает значения, фактически является методом void, определяется в интерфейсе с детализацией как возвращающий Task . Возвращаемое Task значение указывает на асинхронное выполнение и завершение метода. Для методов детализации, не помеченных async ключевым словом, когда метод "void" завершает свое выполнение, он должен вернуть специальное значение Task.CompletedTask :

public Task GrainMethod2()
{
    return Task.CompletedTask;
}

Метод детализации, помеченный как, async возвращает значение напрямую:

public async Task<SomeType> GrainMethod3()
{
    return await GetSomeTypeAsync();
}

voidМетод детализации, помеченный как async , не возвращающий значение, просто возвращается в конце выполнения:

public async Task GrainMethod4()
{
    return;
}

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

public Task<SomeType> GrainMethod5()
{
    Task<SomeType> task = CallToAnotherGrain();

    return task;
}

voidАналогичным образом метод детализации может вернуть возвращаемое значение Task в другой вызов, а не ожидать его.

public Task GrainMethod6()
{
    Task task = CallToAsyncAPI();
    return task;
}

ValueTask<T> можно использовать вместо Task<T> .

Детализация справочника

Ссылка детализации — это прокси-объект, реализующий тот же интерфейс гранулярности, что и соответствующий класс граней. Он инкапсулирует логическое удостоверение (тип и уникальный ключ) целевого Грана. Ссылка на детализацию используется для вызова детализации целевого объекта. Каждый детализированный справочник — это один экземпляр класса граней (один и тот же), но он может создавать несколько независимых ссылок на один и тот же детализацию.

Так как ссылка на детализацию представляет логическую идентификацию целевого объекта, она не зависит от физического расположения граней и остается действительной даже после полного перезапуска системы. Разработчики могут использовать детализированные ссылки, такие как любой другой объект .NET. Он может быть передан в метод, использован в качестве возвращаемого значения метода и т. п. и даже сохранен в постоянное хранилище.

Ссылку на детализацию можно получить, передав идентификатор детализации в IGrainFactory.GetGrain<TGrainInterface>(Type, Guid) метод, где T является интерфейсом граней и key является уникальным ключом детализации в типе.

Ниже приведены примеры того, как получить ссылку на детализацию для интерфейса, IPlayerGrain определенного выше.

Внутри класса граней:

IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);

Из кода клиента Orleans.

IPlayerGrain player = client.GetGrain<IPlayerGrain>(playerId);

Метод детализации вызова метода

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

// Invoking a grain method asynchronously
Task joinGameTask = player.JoinGame(this);

// The await keyword effectively makes the remainder of the
// method execute asynchronously at a later point
// (upon completion of the Task being awaited) without blocking the thread.
await joinGameTask;

// The next line will execute later, after joinGameTask has completed.
players.Add(playerId);

Можно объединить две или более Tasks ; Операция соединения создает новый Task , который разрешается после завершения всех составляющих Task . Этот шаблон удобен, когда детализация требует запуска нескольких вычислений и ожидает завершения всех из них, прежде чем продолжать. Например, детализация внешнего интерфейса, которая создает веб-страницу, состоящие из нескольких частей, может выполнять несколько внутренних вызовов, по одному для каждой части и принимающую Task для каждого результата. В таком случае гранок будет ожидать присоединение всех этих Tasks объектов; при разрешении объединения Task отдельные Task объекты были завершены, и все данные, необходимые для форматирования веб-страницы, были получены.

Пример

List<Task> tasks = new List<Task>();
Message notification = CreateNewMessage(text);

foreach (ISubscriber subscriber in subscribers)
{
    tasks.Add(subscriber.Notify(notification));
}

// WhenAll joins a collection of tasks, and returns a joined
// Task that will be resolved when all of the individual notification Tasks are resolved.
Task joinedTask = Task.WhenAll(tasks);

await joinedTask;

// Execution of the rest of the method will continue
// asynchronously after joinedTask is resolve.

Виртуальные методы

Класс граней может при необходимости переопределять OnActivateAsync и OnDeactivateAsync виртуальные методы; они вызываются средой выполнения Orleans при активации и деактивации каждого гранулярности класса. Это дает коду детализации возможность выполнения дополнительных операций инициализации и очистки. Исключение, OnActivateAsync вызванное ошибкой процесса активации. Хотя OnActivateAsync , если переопределено, всегда вызывается как часть процесса активации детализации, OnDeactivateAsync не гарантируется, что она будет вызываться во всех ситуациях, например, в случае сбоя сервера или другого аномального события. По этой причине приложения не должны полагаться на OnDeactivateAsync выполнение критически важных операций, таких как сохранение изменений состояния. Они должны использовать его только для наиболее эффективного выполнения операций.