我有消息来源,Observable
。对于每个我希望进行HTTP调用的消息,这将产生另一个Observable
,因此我将它们与flatMap
组合在一起,然后将它们汇入某个订阅者。这里是这个场景的代码:
Rx.Observable.interval(1000)
.flatMap (tick) ->
// returns an `Observable`
loadMessages()
.flatMap (message) ->
// also returns and `Observable`
makeHttpRequest(message)
.subscribe (result) ->
console.info "Processed: ", result
这个例子是用coffeescript编写的,但我认为问题陈述对任何其他Rx实现都有效。
我采用这种方法的问题是loadMessages
非常快地产生了很多消息。这意味着,我在很短的时间内发出了很多HTTP请求。这在我的情况下是不可接受的,所以我想将并行HTTP请求的数量限制为10左右。换句话说,当我发出HTTP请求时,我想限制pipelene或应用某种backpresure。
Rx是否有任何标准方法或最佳做法来处理此类情况?
目前我实施了非常简单(并且非常次优)的背压机制,如果系统在处理过程中有太多按摩,则会忽略滴答。它看起来像这样(简化版):
Rx.Observable.interval(1000)
.filter (tick) ->
stats.applyBackpressureBasedOnTheMessagesInProcessing()
.do (tick) ->
stats.messageIn()
.flatMap (tick) ->
// returns an `Observable`
loadMessages()
.flatMap (message) ->
// also returns and `Observable`
makeHttpRequest(message)
.do (tick) ->
stats.messageOut()
.subscribe (result) ->
console.info "Processed: ", result
我不确定,这是否可以做得更好,或者Rx已经有一些机制来处理这种要求。
答案 0 :(得分:2)
这不是严格的背压,这只是限制了并发性。这是一个简单的方法(忽略我可能错误的语法,通过TextArea编码):
Rx.Observable.interval(1000)
.flatMap (tick) ->
// returns an `Observable`
loadMessages()
.map (message) ->
// also returns and `Observable`, but only when
// someone first subscribes to it
Rx.Observable.defer ->
makeHttpRequest(message)
.merge 10 // at a time
.subscribe (result) ->
console.info "Processed: ", result
在C#中,相同的想法是,而不是SelectMany
,它是Select(Defer(x)).Merge(n)
。 Merge(int)
最多订阅了n
个正在进行中的Observable,并将其余内容缓存到以后。我们拥有Defer
的原因是为了让我们在Merge(n)
订阅我们之前不做任何工作。
答案 1 :(得分:1)
听起来你想从队列中拉出而不是推送你的http请求。 Rx真的是正确的技术选择吗?
编辑:
一般情况下,我不会使用Rx设计解决方案,因为我对源事件有完全的命令控制。这不是一个被动的场景。
Rxjs中的背压模块是为了处理您不拥有源流的情况而编写的。你来了。
TPL Dataflow听起来更适合这里。
如果必须使用RX,则可以设置如下循环:如果要限制为X并发事件,请设置主题作为消息源,命令推送({ {1}})X消息进入它。在您的订阅者中,您可以在OnNext处理程序的每次迭代中将新消息推送到主题,直到源耗尽。这保证了飞行中最多X条消息。
答案 2 :(得分:1)
在RXJS中,您可以使用背压子模块
http://rxjs.codeplex.com/SourceControl/latest#src/core/backpressure/
disclaimer
我从未使用过RX版本的JS,但你确实要求实现背压的标准方法,而核心库似乎也支持它。 RX c#还没有这个支持。不知道为什么。