Scala Monix如何使用flatMap操作符的背压?
Monix使用Ack来同步发出的消息,但如果我使用groupBy和flatMap,则内部可观察对象不会跟随Scala Monix如何使用flatMap操作符的背压?,scala,backpressure,monix,Scala,Backpressure,Monix,Monix使用Ack来同步发出的消息,但如果我使用groupBy和flatMap,则内部可观察对象不会跟随源的背压 请参阅此测试代码: import java.util.concurrent.TimeUnit import monix.execution.Scheduler.Implicits.global import monix.execution.Ack.Continue import monix.reactive.{Observable, OverflowStrategy} impor
源的背压
请参阅此测试代码:
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)
...
其中出现一些背压不匹配:
之后:为每条消息睡眠2秒钟…
背压在背压映射之后给出三个项-…
在背压映射之后,如何为每条消息睡眠2秒…
与在背压方面有一对一的关系-…
另一个疑问是:为什么日志中的每一条消息都要睡眠2秒钟输出(0,72),(0,75),(0,78)
,但是这样的事情(0,72),(1,73),(2,74)
谢谢
Monix版本:
“io.monix”%%“monix”%%“3.0.0-RC1”
您看到的行为正是您所期望的
为了快速总结您的应用程序的功能,让我用我的话来解释一下:
您有一个可观察的
生成数字,并对每个元素执行一些副作用
接下来,按\u3
对元素进行分组
接下来,在每组的可观察到的中再做一些副作用(睡眠和写入控制台)
然后,您将flatMap
每个组的可观察的
,生成一个单一的、平面的可观察的
那么,为什么一开始只看到第一组(其中\u%3==0
)将内容打印到控制台***
答案在于flatMap
:当查看可观察的时,您会发现flatMap
的以下描述:
final def flatMap[B](f: (A) ⇒ Observable[B]): Observable[B]
Alias for concatMap.
[...]
想象一下可观察的列表,就像你想一秒钟列表那样:当你关注列表
s时,你会得到一个单一的列表
,首先包含第一个列表
的元素,然后是第二个列表
的元素,依此类推
在Monix中,通过等待在flatMap
(读取:concatMap
)操作中生成的第一个可观察的
发送“已完成”信号,可以实现可观察的
的相同行为。只有这样,第二个可观察的才会被消耗,依此类推
或者简单地说,flatMap
关心生成的可观察的s的顺序。
但是您的flatMap
操作中的Observable
s何时“完成”?为此,我们必须了解groupBy
是如何工作的,因为这就是它们的来源
对于groupBy
来说,尽管Observable
s是惰性评估的,但它必须将传入元素存储在缓冲区中。我对此不是100%确定,但是如果groupBy
像我认为的那样工作,它将,对于任何拉动下一个元素的分组可观察的
,无限期地遍历原始可观察的
,直到它找到属于该组的元素,保存所有之前的元素(但不是必需的)属于该缓冲区中其他组的元素,以供以后使用
所有这一切意味着groupBy
在源Observable
发出完成信号之前无法知道是否找到了组中的所有元素,然后它将使用所有剩余的缓冲元素,然后向分组Observable
发出完成信号
简单地说:Observable
由groupBy
生成的s在源Observable
完成之前不完成。
当将所有这些信息汇总在一起时,您将了解到,只有当源可观测值(您的可观测范围(0130)
)完成时,第一个分组的可观测值也将完成,并且由于平面图
的原因,只有当所有其他分组的可观测值
将被使用
因为我从你的上一个问题知道,你正在尝试构建一个web套接字,使用flatMap
是一个坏主意-你的源可观察的
传入请求永远不会完成,实际上只服务于你遇到的第一个IP地址
您需要做的是使用mergeMap
与concatMap
mergeMap
相比,它不关心元素的顺序,而是使用“先到先得”规则
***:当你读完我的解释,并希望理解groupBy
和flatMap
的工作原理时,你就会明白我为什么写“在开头” 你问题中的语法错误使你很难理解你在问什么。因为我们都是人,没有人希望你能说(或写)一口流利的英语,你能至少试着用更详细的方式解释一下吗?也许也可以不写线程-
,2222-
和=====
,让你的日志信息更具描述性,这样我们就可以理解你想要表达的内容了。@MarkusAppel,你好,我做了一些漂亮的打印信息和变量名。尽管如此,很抱歉英语也很差。谢谢你的耐心!正如您所说,在构建套接字库时出现了问题。保留。mergeMap
对于每个套接字来说都是一个很好的选择,作为分组事件的一个可观察对象。关于groupBy的另一个想法是如何取消订阅断开连接的套接字,避免缓冲区无效,如果远程地址作为组密钥,则可观察?@LoranceChen我认为这在很大程度上取决于您首先如何将套接字包装成可观察的。但是如果您合并分组的可观察的
s,您将无法取消对单个组的订阅-因为所有内容只有一个订户
。但你可能不需要退订——它不会带来任何真正的小鬼