다음을 통해 공유


관찰자

간단한 메시지/응답 패턴이 충분하지 않고 클라이언트가 비동기 알림을 받아야 하는 경우도 있습니다. 예를 들어 친구가 새 인스턴트 메시지를 게시할 때 사용자가 알림을 원할 수 있습니다.

클라이언트 관찰자는 클라이언트의 비동기 알림을 허용하는 메커니즘입니다. 관찰자 인터페이스는 IGrainObserver을(를) 상속해야 하며, 모든 메서드는 void, Task, Task<TResult>, ValueTask 또는 ValueTask<TResult> 중 하나를 반환해야 합니다. 반환 형식으로 void 을 사용하는 것은 구현에서 async void 을 사용하도록 유도할 수 있으므로 권장하지 않습니다. 메서드에서 예외가 throw될 경우 애플리케이션 크래시가 발생할 수 있으므로 이는 위험한 패턴입니다. 대신, 가능한 한 최선을 다해 알림하려는 시나리오의 경우 OneWayAttribute를 관찰자의 인터페이스 메서드에 적용하는 것을 고려해보세요. 이렇게 하면 수신자가 메서드 호출에 대한 응답을 보내지 않고 관찰자의 응답을 기다리지 않고 메서드가 호출 사이트에서 즉시 반환됩니다. 그레인은 그레인 인터페이스 메서드처럼 호출하여 관찰자에게 메서드를 호출합니다. 런타임은 Orleans 요청 및 응답의 배달을 보장합니다. 관찰자의 일반적인 사용 사례는 애플리케이션에서 이벤트가 발생할 때 클라이언트가 알림을 받도록 등록하는 Orleans 것입니다. 이러한 알림을 게시하는 곡물은 관찰자를 추가하거나 제거하는 API를 제공해야 합니다. 또한 일반적으로 기존 구독의 취소를 허용하는 방법을 노출하는 것이 편리합니다.

관찰된 곡물 형식의 개발을 간소화하는 것과 같은 ObserverManager<TObserver> 유틸리티 클래스를 사용할 수 있습니다. 그래인Orleans은 실패 시, 필요에 따라 자동으로 다시 활성화되지만, 클라이언트는 내결함성이 없습니다. 실패한 클라이언트는 복구되지 않을 수도 있습니다. 이러한 이유로 유틸리티는 ObserverManager<T> 구성된 기간 후에 구독을 제거합니다. 활성 클라이언트는 구독을 활성 상태로 유지하기 위해 정기적으로 자동 갱신을 설정해야 합니다.

알림을 구독하려면 클라이언트가 먼저 관찰자 인터페이스를 구현하는 로컬 개체를 만들어야 합니다. 그런 다음 관찰자 팩토리CreateObjectReference에서 메서드를 호출하여 개체를 그레인 참조로 변환합니다. 그런 다음 알림 grain의 구독 함수에 이 참조를 전달할 수 있습니다.

다른 곡물은 이 모델을 사용하여 비동기 알림을 받을 수도 있습니다. 그레인은 IGrainObserver 인터페이스를 구현할 수 있습니다. 클라이언트 구독 사례와 달리 구독 곡물은 단순히 관찰자 인터페이스를 구현하고 자체에 대한 참조를 전달합니다(예: this.AsReference<IMyGrainObserverInterface>()). CreateObjectReference()가 필요하지 않은 이유는 곡물이 이미 주소로 지정할 수 있기 때문입니다.

코드 예제

주기적으로 클라이언트에 메시지를 보내는 곡물이 있다고 가정해 보겠습니다. 간단히 하기 위해 예제의 메시지는 문자열입니다. 먼저 메시지를 받는 클라이언트에서 인터페이스를 정의합니다.

인터페이스는 다음과 같습니다.

public interface IChat : IGrainObserver
{
    Task ReceiveMessage(string message);
}

유일한 특별 요구 사항은 인터페이스가 IGrainObserver로부터 상속되어야 한다는 것입니다. 이제 이러한 메시지를 관찰하려는 클라이언트는 구현하는 클래스를 구현해야 합니다 IChat.

가장 간단한 사례는 다음과 같습니다.

public class Chat : IChat
{
    public Task ReceiveMessage(string message)
    {
        Console.WriteLine(message);
        return Task.CompletedTask;
    }
}

서버에서는 다음으로 이러한 채팅 메시지를 클라이언트에게 보내는 그레인(Grain)을 설정해야 합니다. 또한 클라이언트가 알림을 구독하고 구독을 취소하는 메커니즘도 제공해야 합니다. 구독의 경우 그레인은 유틸리티 클래스 ObserverManager<TObserver>의 인스턴스를 사용할 수 있습니다.

비고

ObserverManager<TObserver> 는 버전 7.0 이후의 Orleans 일부입니다. 이전 버전의 경우 다음 구현 을 복사할 수 있습니다.

class HelloGrain : Grain, IHello
{
    private readonly ObserverManager<IChat> _subsManager;

    public HelloGrain(ILogger<HelloGrain> logger)
    {
        _subsManager =
            new ObserverManager<IChat>(
                TimeSpan.FromMinutes(5), logger);
    }

    // Clients call this to subscribe.
    public Task Subscribe(IChat observer)
    {
        _subsManager.Subscribe(observer, observer);

        return Task.CompletedTask;
    }

    //Clients use this to unsubscribe and no longer receive messages.
    public Task UnSubscribe(IChat observer)
    {
        _subsManager.Unsubscribe(observer);

        return Task.CompletedTask;
    }
}

클라이언트에 메시지를 보내려면 인스턴스의 메서드를 NotifyObserverManager<IChat> 사용합니다. 메서드는 Action<T> 메서드 또는 람다 식을 인수로 받을 수 있습니다(여기서 TIChat 형식입니다). 인터페이스에서 메서드를 호출하여 클라이언트에 보낼 수 있습니다. 이 경우 메서드 ReceiveMessage가 하나뿐이며 서버에서 보내는 코드는 다음과 같습니다.

public Task SendUpdateMessage(string message)
{
    _subsManager.Notify(s => s.ReceiveMessage(message));

    return Task.CompletedTask;
}

이제 서버에는 관찰자 클라이언트에 메시지를 보내는 방법과 구독/구독 취소를 위한 두 가지 방법이 있습니다. 클라이언트는 곡물 메시지를 관찰할 수 있는 클래스를 구현했습니다. 마지막 단계는 이전에 구현된 Chat 클래스를 사용하여 클라이언트에서 관찰자 참조를 만들고 구독 후 메시지를 받도록 하는 것입니다.

코드는 다음과 같습니다.

//First create the grain reference
var friend = _grainFactory.GetGrain<IHello>(0);
Chat c = new Chat();

//Create a reference for chat, usable for subscribing to the observable grain.
var obj = _grainFactory.CreateObjectReference<IChat>(c);

//Subscribe the instance to receive messages.
await friend.Subscribe(obj);

이제 서버의 그레인에서 SendUpdateMessage 메서드를 호출할 때마다 구독된 모든 클라이언트가 메시지를 받습니다. 클라이언트 코드에서 변수 Chatc 인스턴스는 메시지를 수신하고 콘솔에 출력합니다.

중요합니다

CreateObjectReference에 전달된 개체는 WeakReference<T>를 사용하여 보유되므로, 다른 참조가 없으면 가비지 컬렉션 됩니다.

수집하지 않으려는 각 관찰자에 대한 참조를 유지해야 합니다.

비고

관찰자를 호스팅하는 클라이언트가 실패할 수 있고 복구 후에 생성된 관찰자의 ID가 다르기 때문에 관찰자는 본질적으로 신뢰할 수 없습니다. ObserverManager<TObserver> 는 위에서 설명한 대로 관찰자가 정기적으로 다시 제출하여 비활성 관찰자를 제거할 수 있도록 합니다.

실행 모델

IGrainObserver의 구현은 IGrainFactory.CreateObjectReference을 호출하여 등록됩니다. 해당 메서드에 대한 각 호출은 해당 구현을 가리키는 새 참조를 만듭니다. Orleans 는 이러한 각 참조에 하나씩 전송된 요청을 실행하여 완료합니다. 관찰자는 재진입하지 않습니다. 따라서 Orleans 관찰자에 대한 동시 요청을 인터리브하지 않습니다. 여러 관찰자가 동시에 요청을 수신하는 경우 해당 요청은 병렬로 실행될 수 있습니다. AlwaysInterleaveAttribute 또는 ReentrantAttribute 같은 특성은 관찰자 메서드의 실행에 영향을 주지 않으며, 실행 모델을 사용자 지정할 수 없습니다.