缓存和使缓存的 Mono 无效

kob*_*sad 0 java reactive-programming project-reactor spring-webflux spring-webclient

我在尝试缓存MonoWebClient. 代码是这样的:

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));
}
Run Code Online (Sandbox Code Playgroud)

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

Phi*_*lay 5

你可以这样做:

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;
}
Run Code Online (Sandbox Code Playgroud)

这个想法是你Mono<Token>为每次调用返回相同的缓存实例authenticate()。在.cache该缓存的结果为每个订阅检查经营者保证。

具体来说:

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

这一切都假设:

  • getToken() 在订阅者到达之前不做任何工作
  • getToken() 检索每个订阅者的令牌
  • 您只需要一个活动令牌供所有订阅者使用

另请注意,根据您的用例,您可能希望在令牌到期日期之前稍微到期,以解决时钟偏差。即在新令牌实际到期之前抢先检索它,以防止返回Token将在下游有机会使用它之前到期的令牌。