Monix使用Ack同步发出的消息,但是如果我使用groupBy和flatMap,则内部Observable不会遵循source
的背压。
请查看此测试代码:
import java.util.concurrent.TimeUnit
import monix.execution.Scheduler.Implicits.global
import monix.execution.Ack.Continue
import monix.reactive.{Observable, OverflowStrategy}
import org.junit.Test
class MonixBackpressureWithGroupByTest2 {
@Test
def test(): Unit = {
val source = Observable.range(0,130)
val backPressuredStream = source.map(x => {
println("simple log first map - " + x)
x
})
.asyncBoundary(OverflowStrategy.BackPressure(5))
.map { i =>
println("after backpressure map, and Rim 3 operation of source - " + ((i % 3) toString) -> i)
((i % 3) toString) -> i
}
.groupBy{case (k, v) => k}
.flatMap(x => {
val mapWithSleep = x.map{case groupedMsg@(key, value) =>
Thread.sleep(2000)
println("inner Observable after group by rim 3. sleep 2 second for every message - " + groupedMsg)
groupedMsg
}
mapWithSleep
})
backPressuredStream.share.subscribe(
(keyAndValue: (String, Long)) => Continue
)
global.scheduleWithFixedDelay(0L, 1000L, TimeUnit.MILLISECONDS, () => {
println("========sleep 1 second ============")
})
Thread.currentThread().join()
}
}
输出:
...
========sleep 1 second ============
inner Observable after group by rim 3. sleep 2 second for every message - (0,72)
(after backpressure map, and Rim 3 operation of source - 1,73)
(after backpressure map, and Rim 3 operation of source - 2,74)
(after backpressure map, and Rim 3 operation of source - 0,75)
========sleep 1 second ============
========sleep 1 second ============
inner Observable after group by rim 3. sleep 2 second for every message - (0,75)
(after backpressure map, and Rim 3 operation of source - 1,76)
(after backpressure map, and Rim 3 operation of source - 2,77)
(after backpressure map, and Rim 3 operation of source - 0,78)
========sleep 1 second ============
========sleep 1 second ============
inner Observable after group by rim 3. sleep 2 second for every message - (0,78)
(after backpressure map, and Rim 3 operation of source - 1,79)
...
出现一些背压不匹配的情况:
之后:sleep 2 second for every message ...
背压给项after backpressure map - ...
sleep 2 second for every message ...
在背压方面如何与after backpressure map - ...
一对一?
另一个疑问:为什么sleep 2 second for every message
的日志输出(0, 72), (0, 75), (0,78)
却却(0, 72), (1, 73), (2,74)
这样的东西?
谢谢。
Monix版本:
"io.monix" %% "monix" % "3.0.0-RC1"
答案 0 :(得分:2)
您看到的行为正是您所期望的。
为了快速总结您的应用程序的功能,让我用我的话解释一下:
您有一个Observable
生成数字,并对每个元素都有一些副作用。
接下来,您按_ % 3
对元素进行分组。
接下来,您将在每个组的Observable
内进行一些其他操作(休眠和写入控制台)。
然后,您flatMap
每个组的Observable
,得到一个单一的Observable
。
那么,为什么一开始您只看到第一组(其中_ % 3 == 0
)向控制台打印内容? ***
答案在于flatMap
:查看Observable
的{{3}}时,会发现以下flatMap
的描述:
final def flatMap[B](f: (A) ⇒ Observable[B]): Observable[B]
Alias for concatMap.
[...]
想起Observable
秒钟,就像想想List
秒钟一样:当连接List
s时,最终会得到一个包含List
的单个第一个List
的元素,然后是第二个List
的元素,依此类推。
在Monix中,通过等待Observable
(读取:Observable
)操作中产生的第一个flatMap
发送“已完成”来完成concatMap
的相同行为“-信号。只有这样,第二个Observable
才会被消耗,依此类推。
或者,简而言之,flatMap
关心产生的Observable
的顺序。
但是您的 Observable
操作中的flatMap
何时“完成”?为此,我们必须了解groupBy
的工作原理-因为那是它们的来源。
尽管groupBy
被懒惰地评估,但为了使Observable
工作,它必须将传入的元素存储在缓冲区中。我对此不是100%的确定,但是如果groupBy
的工作方式像我认为的那样,它将对拉下一个元素的任何分组的Observable
无限期地遍历原始Observable
直到找到属于该组的元素为止,然后将属于其他组的所有先前(但不是必需)元素保存在该缓冲区中,以供以后使用。
所有这些都意味着groupBy
在源Observable
发信号完成之前,不知道是否找到了组中的所有元素,然后它将使用所有剩余的缓冲元素,然后将完成信号发给已分组的对象Observable
。
用简单的话来说:Observable
产生的groupBy
直到源Observable
完成才完成。
将所有这些信息汇总在一起时,您将了解,只有当源Observable(您的Observable.range(0, 130)
)完成时,第一个分组的Observable
也会完成,并且由于{{1 }},然后将使用所有其他分组的flatMap
。
因为我从上一个问题中知道您正在尝试构建Web套接字,所以使用Observable
是个坏主意-传入请求的来源flatMap
永远不会 >完整,仅有效地服务您将遇到的第一个IP地址。
您需要做的是使用Observable
。与mergeMap
进行比较时,concatMap
并不关心元素的顺序,而是“第一个先到先得”-适用规则。
***:当您到达我的解释的结尾并希望理解mergeMap
和groupBy
的工作原理时,您将理解为什么我写“一开始”!