我有这个 Spring WebFlux 控制器:
@RestController
public class Controller
{
@PostMapping("/doStuff")
public Mono<Response> doStuff(@RequestBody Mono<Request> request)
{
...
}
}
现在,说我想关联来自不同客户端的此控制器的各个请求,以根据Request
对象的某些属性对处理进行分组。
采取1:
@PostMapping("/doStuff")
public Mono<Response> doStuff(@RequestBody Mono<Request> request)
{
return request.flux()
.groupBy(r -> r.someProperty())
.flatMap(gf -> gf.map(r -> doStuff(r)));
}
这将不起作用,因为每个调用都会获得自己的流实例。整个flux()
调用是没有意义的,即使有许多流同时触发,也总是会有一个Request
对象通过流客户。我需要收集的是流中可以在其中进行分组的所有请求之间共享的某些部分,这使我陷入了稍微有点过分设计的代码
参加2:
private AtomicReference<FluxSink<Request>> sink = new AtomicReference<>();
private Flux<Response> serializingStream;
public Controller()
{
this.serializingStream =
Flux.<Request>create(fluxSink -> sink.set(fluxSink), ERROR)
.groupBy(r -> r.someProperty())
.flatMap(gf -> gf.map(r -> doStuff(r)));
.publish()
.autoConnect();
this.serializingStream.subscribe().dispose(); //dummy subscription to set the sink;
}
@PostMapping("/doStuff")
public Mono<Response> doStuff(@RequestBody Request request)
{
req.setReqId(UUID.randomUUID().toString());
return
serializingStream
.doOnSubscribe(__ -> sink.get().next(req))
.filter(resp -> resp.getReqId().equals(req.getReqId()))
.take(1)
.single();
}
这种工作虽然看起来像是我在做我不应该做的事情(或者至少他们觉得不对劲),例如泄漏FluxSink
,然后在订阅时通过它注入值,添加一个请求ID,以便随后我可以过滤正确的响应。另外,如果在serializingStream
中发生错误,那么它将破坏每个人的一切,但是我想我可以尝试隔离错误以使事情继续进行。
问题是,有没有一种更好的方法可以像开胸手术一样。
另外,类似情况的相关问题。我当时正在考虑使用Akka Persistence
来实现事件源,并从Reactor
流中进行触发。我在阅读有关Akka Streams
的内容,该内容可以包装一个Actor,然后有一些方法可以将其转换为可以与Reactor
(又称为Publisher
或Subscriber
)连接的内容,但是如果每个请求都得到自己的请求,那么我会有效地减轻背压,并且由于充斥了Persistent Actor的邮箱而冒着OOME的风险,因此我想问题属于上述类别。