那些熟悉lmax ring buffer (disruptor)的人知道,该数据结构最大的优势之一就是它批准了事件,当我们有一个可以利用批处理的消费者时,系统可以自动调整到加载,你抛出的事件越多越好。
我想我们无法通过Observable实现相同的效果(定位批处理功能)。我已经尝试了Observable.buffer,但这是非常不同的,缓冲区将等待并且在预期的事件数量没有到达时不发出批处理。我们想要的是完全不同的。
鉴于subriber正在等待来自Observable<Collection<Event>>
的批次,当单个项目到达流时,它会发出一个由订阅者处理的单个元素批处理,而正在处理的其他元素正在到达并被收集到下一个批处理,一旦订阅者完成执行,它就会获得自上次开始处理以来已经到达的事件数量的下一批次......
因此,如果我们的订户足够快以一次处理一个事件,它将会这样做,如果负载变高,它仍将具有相同的处理频率但每次都有更多事件(从而解决背压问题)。 ..不像缓冲区那样会坚持并等待批次填满。
有什么建议吗?或者我应该使用环形缓冲区?
答案 0 :(得分:6)
RxJava和Disruptor代表两种不同的编程方法。
我没有使用Disruptor,但基于视频会谈,它基本上是一个大型缓冲区,生产者发出数据,如firehose和消费者旋转/产量/阻止,直到数据可用。
另一方面,RxJava旨在实现非阻塞事件传递。我们也有ringbuffers,特别是在observeOn中,它充当生产者和消费者之间的异步边界,但是它们要小得多,我们通过应用协同例程方法避免缓冲区溢出和缓冲膨胀。协同程序归结为发送到回调的回调,因此你可以回调我们的回调,按你的节奏发送一些数据。此类请求的频率决定了起搏。如果下游没有足够快的请求,有些数据源不支持此类合作流,并且需要其中一个onBackpressureXXX
运算符来缓冲/丢弃值。
如果您认为可以比一个一个更有效地批量处理数据,则可以使用具有重载的buffer
运算符来指定缓冲区的持续时间:例如,您可以拥有10个ms的数据,与此持续时间内有多少值无关。
通过请求频率控制批量大小非常棘手,可能会产生不可预见的后果。通常,问题是,如果来自批处理源的request(n)
,则表明您可以处理n个元素,但源现在必须创建大小为1的n个缓冲区(因为类型为Observable<List<T>>
)。相反,如果没有调用请求,则运算符缓冲数据,从而产生更长的缓冲区。这些行为会在处理中引入额外的开销,如果你真的可以跟上并且还必须将冷源变成一个firehose(因为否则你所拥有的基本上是buffer(1)
),这本身现在可能导致缓冲膨胀。