조직 개발

조직 클래스를 구현하는 코드를 작성하기 전에 .NET Standard 또는 .NET Core(기본 설정) 또는 .NET Framework 4.6.1 이상을 대상으로 하는 새 클래스 라이브러리 프로젝트를 만듭니다(종속성으로 인해 .NET Standard 또는 .NET Core를 사용할 수 없는 경우). 구현에서 인터페이스를 더 잘 분리하기 위해 동일한 클래스 라이브러리 프로젝트 또는 두 개의 서로 다른 프로젝트에서 조직 인터페이스와 조직 클래스를 정의할 수 있습니다. 두 경우 모두 프로젝트는 Microsoft.Orleans.Core.AbstractionsMicrosoft.Orleans.CodeGenerator.MSBuild NuGet 패키지를 참조해야 합니다.

자세한 지침은 자습서 1 – Orleans 기본 사항프로젝트 설정 섹션을 참조하세요.

조직 인터페이스 및 클래스

조직은 서로 상호 작용하고 각 조직 인터페이스의 일부로 선언된 메서드를 호출하여 외부에서 호출됩니다. 조직 클래스는 이전에 선언된 하나 이상의 조직 인터페이스를 구현합니다. 조직 인터페이스의 모든 메서드는 Task(void메서드의 경우), Task<TResult> 또는 ValueTask<TResult>(형식 T의 값을 반환하는 메서드의 경우)를 반환해야 합니다.

다음은 Orleans 버전 1.5 Presence Service 샘플에서 발췌한 내용입니다.

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();
}

값이 반환되지 않는 async로 표시된 void 조직 메서드는 실행이 끝날 때 반환됩니다.

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;
}

Task<T> 대신 ValueTask<T>를 사용할 수 있습니다.

조직 참조

조직 참조는 해당 조직 클래스와 동일한 조직 인터페이스를 구현하는 프록시 개체입니다. 대상 조직의 논리적 ID(형식 및 고유 키)를 캡슐화합니다. 조직 참조는 대상 조직을 호출하는 데 사용됩니다. 각 조직 참조는 단일 조직(조직 클래스의 단일 인스턴스)에 대한 것이지만 동일한 조직에 대해 여러 개의 독립적인 조직을 만들 수 있습니다.

조직 참조는 대상 조직의 논리적 ID를 나타내기 때문에 해당 조직의 물리적 위치와 독립적이며 시스템을 완전히 다시 시작한 후에도 유효한 상태를 유지합니다. 개발자는 다른 .NET 개체와 마찬가지로 조직 참조를 사용할 수 있습니다. 메서드에 전달되어 메서드 반환 값 등으로 사용하며 영구 스토리지에 저장할 수도 있습니다.

조직 참조는 조직의 ID를 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.

가상 메서드

조직 클래스는 선택적으로 OnActivateAsyncOnDeactivateAsync 가상 메서드를 재정의할 수 있습니다. 이러한 메서드는 클래스의 각 조직을 활성화 및 비활성화할 때 Orleans 런타임에 의해 호출됩니다. 이렇게 하면 조직 코드에서 추가 초기화 및 정리 작업을 수행할 수 있습니다. OnActivateAsync에서 예외가 throw된 경우 활성화 프로세스가 실패합니다. 재정의되는 경우 OnActivateAsync는 항상 조직 활성화 프로세스의 일부로 호출되지만 OnDeactivateAsync는 예를 들어 서버 오류 또는 기타 비정상적인 이벤트와 같은 모든 상황에서 호출된다는 보장이 없습니다. 따라서 애플리케이션은 상태 변경의 지속성과 같은 중요한 작업을 수행하는 데 OnDeactivateAsync를 사용해서는 안 됩니다. 최선의 노력을 해야 하는 작업에만 사용해야 합니다.