这个问题很难用文字描述,所以如果标题不符合要求,抱歉。
我想通过 Project Reactor Flux 和 Mono 实现一个特定目标,乍一看似乎非常简单。
一个代码示例,在“阻塞方式”中会比长描述更好:
fun findGroupToCreateBlocking(userId: UUID, groupLabel: String): Optional<LinkUserToGroup> {
val group = lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.blockOptional()
if(group.isPresent) {
return Optional.empty()
}
return lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
.blockOptional()
}
我尝试在没有
block
部分的情况下实现同样的目标。我最终得到了以下代码:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.map { Optional.of(it) }
.defaultIfEmpty(Optional.empty())
.filter { g -> g.isEmpty }
.flatMap { lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
我认为(而且我不是唯一一个😇)我们可以做得更好,而不是依赖于流中间的
Optional
用法...但我没有找到任何其他解决方案。
这是我第四次与这种“模式”作斗争,所以欢迎一些帮助!
我在 Gitlab 上生成了一个演示项目(here),其中包含针对反应式和阻塞式实现的单元测试,以查看命题是否符合要求。如果需要,您可以分叉并使用该项目。
我没有使用
Mono.empty
,而是使用 Flux.hasElement
方法(如 @yossarian),但添加了一个否定过滤器。由于单元测试仍然通过,它似乎可以工作。
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.map { it.label }
.hasElement(groupLabel)
.filter { g -> !g }
.flatMap { lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
由于我们只想在用户不属于某个组时搜索组,因此使用否定过滤器可以使这一点变得更加明确。
我没有找到一个很好的 Reactor 解决方案。但是,由于您使用的是 Kotlin,您可能会接受该模式并为其创建扩展函数:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.switchIfEmptyOrEmpty {
lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
fun <T> Mono<*>.switchIfEmptyOrEmpty(monoIfEmpty: () -> Mono<T>): Mono<T> =
this.map { Optional.of(it) }
.defaultIfEmpty(Optional.empty())
.filter { g -> g.isEmpty }
.flatMap { monoIfEmpty.invoke() }
hasElement 运算符的另一种替代方法:
fun findGroupToCreateReactive(userId: UUID, groupLabel: String): Mono<LinkUserToGroup> =
lib.findGroupsOfUser(userId)
.flatMapIterable { it.items }
.filter { it.label == groupLabel }
.toMono()
.hasElement()
.flatMap { hasElement ->
if (hasElement)
{
return@flatMap Mono.empty<LinkUserToGroup>()
} else
{
lib.searchGroups(groupLabel)
.flatMapIterable { it.items }
.toMono()
.map { LinkUserToGroup(userId, it.id) }
.switchIfEmpty { IllegalStateException("Group $groupLabel not found").toMono() }
}
}
PS:感谢您提供示例存储库。这确实有助于尝试不同的事情!