缓存和使缓存的Mono无效

时间:2019-11-14 22:05:13

标签: java reactive-programming spring-webflux project-reactor spring-webclient

尝试缓存Mono返回的WebClient时遇到问题。代码是这样的:

public Mono<Token> authenticate() {
    return cachedTokenMono = cachedTokenMono
        .switchIfEmpty(
            Mono.defer(() -> 
                    getToken())
                    .cache(token ->
                               Duration.between(Instant.now(), token.getExpires().toInstant()),
                           (Throwable throwable) -> Duration.ZERO,
                           () -> Duration.ZERO));
}

目的是缓存用于接收Mono的{​​{1}},直到令牌过期为止。令牌过期后,缓存的Token将变为空,并请求一个新令牌。 这可以按预期工作,但不幸的是Mono实际上并没有“切换”,而是包装了源switchIfEmpty()。结果,随着创建越来越多的包装Mono,这会导致内存泄漏。 在这种情况下正确的模式是什么?有没有办法用新的SwitchIfEmptyMono代替空的{}?

1 个答案:

答案 0 :(得分:2)

您可以执行以下操作:

private final Mono<Token> authenticateMono = getToken()
            .cache(
                    token -> Duration.between(Instant.now(), token.getExpires().toInstant()),
                    throwable -> Duration.ZERO,
                    () -> Duration.ZERO)

public Mono<Token> authenticate() {
    return authenticateMono;
}

这个想法是,您对Mono<Token>的每次调用都返回相同的缓存authenticate()实例。 .cache运算符确保为每个订阅检查缓存的结果。

特别是:

  • 如果新订阅到达并且没有缓存值,那么缓存操作员将订阅从Mono<Token>返回的getToken()(这将触发令牌检索)。
  • 如果已经缓存了一个值,并且在缓存超时之前 到达了新的订阅,则缓存操作员会将缓存的值发送给新订阅者
  • 如果已缓存一个值,并且在缓存超时后 后有新的订阅到达,则缓存操作员将重新订阅从Mono<Token>返回的getToken()(将会触发令牌的重新检索。
  • 如果从Mono<Token>返回的getToken()发生异常,则该异常将不会被缓存,因此将传播,并且下一个订阅将再次触发令牌检索

所有这些都假定:

  • getToken()在订户到达之前不做任何工作
  • getToken()检索每个订户的令牌
  • 您只希望为所有订阅者使用一个有效令牌

还请注意,根据使用情况,您可能希望在令牌的到期日期之前将其过期,以解决时钟偏移问题。也就是说,要在新令牌实际到期之前抢先检索新令牌,以防止返回Token,该令牌在下游有机会使用之前就已经到期。