Orleans 2.0 官方文档 —— 4.5 Grains -> 观察者

观察者

在某些情况下,简单的消息/响应模式是不够的,客户端需要接收异步通知。例如,用户可能希望在朋友发布新的即时消息时得到通知。

客户端的观察者是一种允许异步通知客户端的机制。观察者是从IGranobserver继承的单向异步接口,它的所有方法都必须是void。grain通过像grain接口方法一样,调用它向观察者发送通知,只不过它没有返回值,因此grain不需要依赖于结果。Orleans运行时会确保单向传递通知。发布此类通知的grain,应该提供一个API来添加或删除观察者。此外,通常也很方便地暴露一个允许取消现有订阅的方法。grain开发者可以使用Orleans ObserverSubscriptionManager<T>泛型类,来简化被观察的grain类型的开发。

要订阅通知,客户端必须首先创建一个实现观察者接口的本地C#对象。然后,它在观察器工厂上调用一个静态方法CreateObjectReference(),将C#对象转换为一个grain引用,然后可以将该grain引用传递给通知grain上的订阅方法。

其他grain也可以使用此模型来接收异步通知。与客户端订阅不同,订阅grain只是将观察者接口实现为门面,并将引用传递给自身(例如this.AsReference<IMyGrainObserverInterface>)。

代码示例

假设我们有一个定期向客户端发送消息的grain。为简单起见,我们示例中的消息将是一个字符串。我们首先在客户端上定义将接收消息的接口。

接口看起来像这样

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

唯一特别的是,接口应该继承IGrainObserver。现在,任何想要观察这些消息的客户端,都应该实现一个类,而该类实现IChat

最简单的情况是这样的:

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

现在在服务器上,我们应该有一个grain,它将这些聊天消息发送给客户端。grain还应该有一个机制,让客户端订阅和取消订阅自己来接收通知。对于订阅,grain可以使用工具类ObserverSubscriptionManager。如果您试图订阅已订阅的观察者(或取消订阅一个未订阅的观察者),则此类会抛出一个OrleansException,因此,对此次情况,使用IsSubscribed()方法,或处理OrleansException,就很重要:

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

    public override async Task OnActivateAsync()
    {
        // We created the utility at activation time.
        _subsManager = new ObserverSubscriptionManager<IChat>();
        await base.OnActivateAsync();
    }

    // Clients call this to subscribe.
    public Task Subscribe(IChat observer)
    {
        if (!_subsManager.IsSubscribed(observer))
        {
            _subsManager.Subscribe(observer);
        }
        return Task.CompletedTask;
    }

    //Also clients use this to unsubscribe themselves to no longer receive the messages.
    public Task UnSubscribe(IChat observer)
    {
        if (_subsManager.IsSubscribed(observer))
        {
            _subsManager.Unsubscribe(observer);
        }
        return Task.CompletedTask;
    }
}

要将消息发送到客户端,可以使用ObserverSubscriptionManager<IChat>实例的Notify方法。该方法接收一个Action<T>方法或lambda表达式(此处TIChat类型)。您可以调用接口上的任何方法将其发送给客户端。在我们的例子中,我们只有一个方法ReceiveMessage,我们在服务器上的发送代码如下所示:

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

现在,我们的服务器有一个向观察者客户端发送消息的方法,两种方法用于订阅/取消订阅,客户端实现了一个类,以便能够观察grain消息。最后一步是使用我们之前实现的Chat类,在客户端上创建一个观察者引用,并让它在订阅之后接收消息。

代码如下所示:

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

//Create a reference for chat usable for subscribing to the observable grain.
var obj = await GrainClient.GrainFactory.CreateObjectReference<IChat>(c);
//Subscribe the instance to receive messages.
await friend.Subscribe(obj);

现在,只要服务器上的grain调用该SendUpdateMessage方法,所有订阅的客户端都将收到该消息。在我们的客户端代码中,变量c中的Chat实例,将接收消息并将其输出到控制台。

注意:传递给CreateObjectReference的对象是通过一个WeakReference<T>来保存的,因此,如果不存在其他引用,则将被垃圾收集。用户应该为每个不希望被垃圾收集的观察者,保留一个引用。

注意:观察者本质上是不可靠的,因为您不会得到任何响应,来知道消息是被接收和处理,或者由于分布式系统中可能出现的任何情况而导致消息失败。因此,您的观察者应该定期对grain进行轮询,或使用任何其他机制,来确保他们收到了他们应该收到的所有消息。在某些情况下,您可以承受丢失某些消息的代价,并且不需要任何额外的机制,但如果您需要确保所有观察者始终接收消息,并且正在接收所有消息,则定期重新订阅并轮询观察者grain,都有助于确保最终处理所有消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值