我是Webflux的新手,正在尝试实现以下方案:
客户要求数据
如果Redis缓存中已经存在数据=>返回缓存的数据
否则查询远程服务以获取数据
我写了这段代码:
ReactiveRedisOperations<String, Foo> redisOps;
private Mono<Foo> getFoo(String k) {
return this.redisOperations.opsForValue().get(k)
.map(f -> this.logCache(k, f))
.switchIfEmpty(this.queryRemoteService(k));
}
private void logCache(String k, Foo f) {
this.logger.info("Foo # {} # {} present in cache. {}",
k,
null != f ? "" : "NOT",
null != f ? "" : "Querying remote");
}
private Mono<Foo> queryRemoteService(String k) {
this.logger.info("Querying remote service");
// query code
}
它打印:
"Querying remote service"
"Foo # test_key # present in cache"
如何确保仅当不存在缓存的数据时才调用
switchIfEmpty
?编辑
根据Michael Berry的回答,我将代码重构如下:
private Mono<Foo> getFoo(String k) {
this.logger.info("Trying to get cached {} data", k);
this.logger.info(this.redisOps.hasKey(k).block() ? "PRESENT" : "NOT present");
return this.redisOperations.opsForValue().get(k)
.switchIfEmpty(this.queryRemoteService(k));
}
private Mono<Foo> queryRemoteService(String k) {
this.logger.info("Querying remote service");
// query code
}
现在我的输出是这样的:
Trying to get cached test_key data
PRESENT
Querying provider
因此,似乎只执行了一次,但仍然无法避免执行switchIfEmpty。我确定redis包含该密钥的数据
最佳答案
这行:
.map(f -> this.logCache(k, f))
...很奇怪,因为您没有将任何内容映射到任何内容,而是在执行副作用(记录值)。在这里,
doOnNext()
(而不是map()
)将是一个更明智的选择。但是,我离题了,这不是这里的主要问题。
如何确保仅当不存在缓存的数据时才调用
switchIfEmpty
?就是这样,但是您的日志记录并未按照您的想法进行。这里可能引起您问题的关键概念是,
null
永远不会通过响应流传播。如果像在本示例中那样流为空,则什么都不会传播。因此,在您的代码中,不会调用
map()
(也不会使用doOnNext()
),因此您的“存在于缓存中”行将不会写入日志(因为没有要映射的值,也没有因此,在logCache()
中检查该值是否为空是没有意义的-它永远不会为空。据我所知,因此,此处的日志输出必须是两次
getFoo()
调用的结果:第一个不在高速缓存中,因此未调用
map()
,未切换switchIfEmpty()
,并打印了“查询远程服务”。第二个存在于缓存中,因此打印了“存在于缓存中”行,未调用
switchIfEmpty()
,因此未显示“查询远程服务”。为了使您的日志记录有意义,您应该从
logCache()
中删除条件逻辑,并在queryRemoteService()
方法中添加“在缓存中不存在”行。关于java - 如何开启单声道,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58974349/