如何在被动扩展中创建Hot observable

时间:2016-05-31 07:46:37

标签: oracle event-handling system.reactive reactive-programming oracle-aq

我正在尝试使用Oracle AQ的Reactive扩展。当Oracle Queue上出现消息时,它会触发" OracleAQMessageAvailableEvent"告诉消费者有消息。在OracleAQMessageAvailableEventHandler中,使用者调用OracleAQQueue.Dequeue()来检索消息。

我已经完成了以上的RX工作。以下是我使用过的代码。

var messages = Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
                    h => _queue.MessageAvailable += h, h => _queue.MessageAvailable -= h)
                .Where(x => x.EventArgs.AvailableMessages > 0)
                .Select(x =>
                {
                    OracleAQMessage msg = _queue.Dequeue();
                    return (UpdateMsg) msg.Payload;
                });
messages.subscribe(....)

问题是,如果我订阅了一切,但是如果我多次订阅消息(即我的应用程序中有多个消费者),那么每个消费者都会尝试调用&#34; _queue.Dequeue()&#34;如果我们没有新消息,第一次通话后的每次通话都将失败。

有人可以指导我该怎么做。我想,我的场景是针对Hot Observable,但我很难理解它。

2 个答案:

答案 0 :(得分:3)

我认为你正在寻找一个Hot Observable是正确的。 如果我们遵循代码,可能会更清楚为什么您会多次调用_queue.Dequeue();

首先,您订阅Oracle的活动

Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
    h => _queue.MessageAvailable += h, 
    h => _queue.MessageAvailable -= h)

这就像挂接前Rx世界中的事件处理程序一样。 每个人听(订阅)都会收到相同的事件。 如果他们在提出事件后订阅,那么他们就错过了。

然后你过滤掉空集。

.Where(x => x.EventArgs.AvailableMessages > 0)

那里没什么特别的。

然后从查询中执行副作用。

.Select(x =>
    {
        OracleAQMessage msg = _queue.Dequeue();
        return (UpdateMsg) msg.Payload;
    });

这里的副作用是您正在进行破坏性阅读(Dequeue)。 所有订阅者从上游_queue.MessageAvailable推送活动时都会尝试拨打Dequeue()

为避免所有订阅者调用副作用,您可以将序列设为Hot(如您所建议的那样)。 为此,您可以查看Publish()运算符。

Publish()运算符会通过添加IConnectableObservable<T>方法向您返回IObservable<T>,只展开Connect()。 这允许对订阅逻辑执行时进行细粒度控制。 但是,这对你来说可能有太多的控制,你可能会发现RefCount()正是你所需要的。

Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
    h => _queue.MessageAvailable += h, 
    h => _queue.MessageAvailable -= h)
.Where(x => x.EventArgs.AvailableMessages > 0)
.Select(x =>
    {
        OracleAQMessage msg = _queue.Dequeue();
        return (UpdateMsg) msg.Payload;
    })
.Publish()
.Refcount();

现在,您的每个订阅者都会收到相同的消息,并且每个事件只会调用一次Dequeue()副作用(并且只有订阅者时才会调用)。

热和冷可观察到here

答案 1 :(得分:-1)

李坎贝尔,对不起我的坏话。你提到的解决方案确实有效。实际上,我错误地使用它。我有一个类调用QueueWrapper,它有一个名为Messages的属性。我有消息的这种实现

    public IObservable<UpdateMsg> Messages { 
        get { return Observable.FromEventPattern<OracleAQMessageAvailableEventHandler,         OracleAQMessageAvailableEventArgs> (
        h => _queue.MessageAvailable += h, 
        h => _queue.MessageAvailable -= h)
       .Where(x => x.EventArgs.AvailableMessages > 0)
       .Select(x =>
        {
            OracleAQMessage msg = _queue.Dequeue();
            return (UpdateMsg) msg.Payload;
        })
       .Publish()
       .Refcount();
}}

我的客户端代码是使用Messages属性订阅的,如此

// First Subscription
_queueWrapper.Messages.Subscribe(....)

// Second Subscription
_queueWrapper.Messages.Subscribe(....)

因此,对于每个订阅,Messages属性都返回一个新的IObservable。为了解决这个问题,我将observable的初始化移到了QueueWrapper的构造函数,即代码:

    public QueueWrapper() {
     _messages = Observable.FromEventPattern<OracleAQMessageAvailableEventHandler, OracleAQMessageAvailableEventArgs> (
        h => _queue.MessageAvailable += h, 
        h => _queue.MessageAvailable -= h)
    .Where(x => x.EventArgs.AvailableMessages > 0)
    .Select(x =>
        {
            OracleAQMessage msg = _queue.Dequeue();
            return (UpdateMsg) msg.Payload;
        })
    .Publish()
    .Refcount();
}

我的Messages属性只返回_messages;

public IObservable<UpdateMsg> Messages { get { return _messages; } }

之后,一切都按预期开始工作。