我目前正在使用 quarkus 编写一个响应式 Web 应用程序。每隔一段时间,我就会发现自己陷入了一种我称之为“连续地狱”的境地,我将多个调用链接到数据库,直到获得响应所需的所有数据。
以一个端点为例,该端点从三个 Panache 实体
Foo
、Bar
和 Baz
读取数据,并使用这些实体的数据创建一个 MyDto
实例。对于这个端点,我会编写如下代码:
@GET
public Uni<MyDto> get(@QueryParam("criteria") String someCriteria) {
return Foo.findBySomeCriteria(someCriteria)
.chain(foo -> Bar.findBySomeCriteria(someCriteria)
.chain(bar -> Baz.findBySomeCriteria(someCriteria)
.map(baz -> new MyDto(
foo.value,
bar.value,
baz.value
))
)
);
}
虽然这是一个微不足道的示例,但我已经将一些重要的缩进和样板代码与我的(最小)业务逻辑混合在一起。这使得代码难以阅读。当我添加错误处理或特殊情况时,它只会变得更糟糕。
不幸的是,我无法合并 Panache 返回的
Uni
。这会给我一个IllegalStateException
,并显示以下消息:“非法 pop() 与不匹配的 JdbcValuesSourceProcessingState”,因为不可能在反应式 Hibernate 会话上同时启动两个操作。否则,解决方案可能如下所示:
@GET
public Uni<MyDto> get(@QueryParam("criteria") String someCriteria) {
// This does not work !!!
Uni<Foo> fooUni = Foo.findBySomeCriteria(someCriteria);
Uni<Bar> barUni = Bar.findBySomeCriteria(someCriteria);
Uni<Baz> bazUni = Baz.findBySomeCriteria(someCriteria);
return Uni.combine().all().unis(fooUni, barUni, bazUni)
.with((foo, bar, baz) -> new MyDto(
foo.value,
bar.value,
baz.value
));
}
那么有什么好方法可以逃离这个“持续地狱”呢?
在更详细地检查 Mutiny Reference 后,我发现在组合
Uni
时可以限制并发上游订阅的数量。通过将并发设置为1
,上游Uni
将顺序执行,从而避免了与Hibernate相关的问题。不幸的是,这种方法意味着我们丢失了有关元素的类型信息,因此我们需要在订阅中重新创建它。
因此,组合
Uni
的第二个示例将像这样工作:
@GET
public Uni<MyDto> get(@QueryParam("criteria") String someCriteria) {
Uni<Foo> fooUni = Foo.findBySomeCriteria(someCriteria);
Uni<Bar> barUni = Bar.findBySomeCriteria(someCriteria);
Uni<Baz> bazUni = Baz.findBySomeCriteria(someCriteria);
return Uni.combine().all().unis(fooUni, barUni, bazUni)
.usingConcurrencyOf(1)
.with(results -> new MyDto(
((Foo) result.get(0)).value,
((Bar) result.get(1)).value,
((Baz) result.get(2)).value
));
}
除了类型问题之外,这正是我正在寻找的。