我尝试基于反应堆栈(Reactor + WebFlux)创建一些基本的Spring 5应用程序。
我的下一个目标是实现能够:
的反应式存储库我的存储库需要涵盖以下场景:
情景A:
所以,IMO这个场景是冷源和热源概念的混合。在任何人订阅之前,我们会收集某人在我们的存储库中保存在某个缓冲区中的数据(比如普通的List)。对于将订阅FindAll的所有订阅者,我们需要推送缓冲列表(在订阅之前收集)并且不要完成流以允许推送以后的收集更新。
我能够实现这一目标,但我仍然认为有更简单的方法吗?也许在Reactor项目中有一个已经涵盖这种情况的解决方案?
我的实施:
public class InMemoryBookRepository {
private final Map<String, Book> bookMap = new ConcurrentHashMap<>();
private final UnicastProcessor<Book> processor = UnicastProcessor.create();
private final FluxSink<Book> fluxSink = processor.sink(FluxSink.OverflowStrategy.LATEST);
private final Flux<Book> hotFlux = processor.publish().autoConnect();
@Override
public void save(Book book) {
bookMap.put(book.getId(), book);
fluxSink.next(book);
}
@Override
public Flux<Book> findAll() {
//without fromIterable I cannot push books that where saved before someone subscribed
return Flux.fromIterable(bookMap.values())
.concatWith(hotFlux)
//Unfortunately this solution produces duplicates so we need to filter them
.distinct();
}
}
Ofc,我不能只使用Cold发布者 - 因为流将在发布收集的图书后完成。出于同样的原因,我不能使用Hot,因为我会错过在某人订阅之前生成的元素。
旁注:在我的代码中,我的地图没有任何清理机制,所以它会在某些时候产生异常,但现在这并不重要。
答案 0 :(得分:0)
这么简单......我不知道为什么我错过了这个漂亮的算子:https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#replay--
所以基本上删除代码的整个let validJsonResponseData = """
{"message" : "A message"}
""".data(using: .utf8)!
let simpleTextResponseData = "Error".data(using: .utf8)!
struct Response: Decodable {
let message: String
}
do {
let successResponse = try JSONDecoder().decode(Response.self, from: validJsonResponseData)
let errorResponse = try JSONDecoder().decode(Response.self, from: simpleTextResponseData)
} catch {
if let error = error as? DecodingError {
switch error {
case .dataCorrupted(_):
if let simpleTextResponse = String(data: simpleTextResponseData, encoding: .utf8) {
let errorResponse = Response(message: simpleTextResponse)
}
default:
throw error
}
} else {
throw error
}
}
部分并使用List/Map
代替replay()
简化示例:
publish()
使用UnicastProcessor<String> processor = UnicastProcessor.create();
FluxSink<String> fluxSink = processor.sink(FluxSink.OverflowStrategy.LATEST);
//change 'publish()' to 'replay()'
Flux<String> hotFlux = processor.publish().autoConnect();
hotFlux.subscribe(n -> log.info("1st subscriber: {}", n));
fluxSink.next("one");
hotFlux.subscribe(n -> log.info("2nd subscriber: {}", n));
fluxSink.next("two");
输出:
publish()
使用1st subscriber: one
1st subscriber: two
2nd subscriber: two
输出:
replay()