Java 线程安全反应式缓存

Java 线程安全反应式缓存,java,rx-java,rx-java2,project-reactor,Java,Rx Java,Rx Java2,Project Reactor,一旦我不得不开始打多个外部电话,我总是被被动的思维方式所困扰。现在我的任务是使用Reactor和Webflux实现以下场景 从缓存中获取令牌 找不到令牌 来自身份验证系统新令牌的请求 在缓存中持久化令牌 找到代币 使用令牌请求外部系统 外部系统响应2xx 返回响应 外部系统响应403 使缓存中的令牌无效 从顶部重试整个流 到目前为止,我只举了一个关于如何使用缓存的示例: public static Mono<ServerResponse> doRequest(

一旦我不得不开始打多个外部电话,我总是被被动的思维方式所困扰。现在我的任务是使用Reactor和Webflux实现以下场景

  • 从缓存中获取令牌
  • 找不到令牌
  • 来自身份验证系统新令牌的请求
  • 在缓存中持久化令牌
  • 找到代币
  • 使用令牌请求外部系统
  • 外部系统响应2xx
  • 返回响应
  • 外部系统响应403
  • 使缓存中的令牌无效
  • 从顶部重试整个流
  • 到目前为止,我只举了一个关于如何使用缓存的示例:

      public static Mono<ServerResponse> doRequest(
          Function<Mono<Token>, Mono<ClientResponse>> clientRequest,
          Function<Mono<ClientResponse>, Mono<ServerResponse>> errorHandler
      ) {
        Token token = null;
        return token.getToken()
            .transform(clientRequest)
            .flatMap(clientResponse -> {
                  if (clientResponse.statusCode().value() == 403) {
                    // HOW WOULD I INVALIDATE ANYTHING HERE? Or where should i do it?
                    return Mono.error(new TokenExpired("Token expired"));
                  }
                  return Mono.just(clientResponse);
            })
            .transform(errorHandler)
            .retryWhen(companion -> {
          return companion.zipWith(Flux.range(1,4),
              (error, index) -> {
                if (index < 4 && (error instanceof TokenExpired)) {
                  return index;
                } else {
                  throw Exceptions.propagate(error);
                }
              });
        });
      }
    
    publicstaticmonodorequest(
    功能clientRequest,
    函数错误处理程序
    ) {
    令牌=null;
    return-token.getToken()
    .transform(clientRequest)
    .flatMap(客户端响应->{
    if(clientResponse.statusCode().value()==403){
    //我该如何使这里的任何东西无效?或者我该在哪里做?
    返回Mono.error(新令牌已过期(“令牌已过期”);
    }
    返回Mono.just(clientResponse);
    })
    .transform(errorHandler)
    .retryWhen(同伴->{
    返回伴星zipWith(通量范围(1,4),
    (错误,索引)->{
    if(索引<4&(令牌过期的错误实例)){
    收益指数;
    }否则{
    抛出异常。传播(错误);
    }
    });
    });
    }
    
    我查看了缓存,但未能理解如何手动使缓存无效,因为它只基于时间?此外,在多线程环境中,当使用咖啡因缓存时,invalidate的行为是未定义的。我觉得我的用例是标准的,但一直无法找到任何模式来实现这一点

    我遇到的两个问题是: 确保缓存的更新只发生一次并且是非阻塞的。 如何以非阻塞方式使缓存中的条目无效


    我真的被困在如何以反应的方式处理问题上,一个公认的答案不一定是反应器,它可以是任何反应库,展示解决问题所需的思想

    当计算失败或为空时,咖啡因将丢弃一个条目,并将其传播给调用者。对于
    AsyncCache
    ,它会立即存储未来,并在必要时执行
    whenComplete
    回调进行错误处理。由于咖啡因与CompleteableFuture配合使用,所以您可以使用反应堆的mono/future转换器

    AsyncCache-responseCache=caffee.newBuilder().buildAsync();
    公共静态Mono-doRequest(
    功能clientRequest,
    函数errorHandler){
    令牌=/。。。
    Mono result=Mono.fromFuture(()->token.getToken()
    .transform(t->cache.get(t,(令牌,执行器)->{
    Mono响应=clientRequest.apply(令牌);
    返回(response.statusCode().value()==HttpServletResponse.SC\u禁止)
    ?Mono.错误(新令牌过期(“令牌过期”)).toFuture()
    :Mono.just(clientResponse.toFuture();
    })));
    返回result.transform(errorHandler.retryWhen(…);
    }
    
    只有当映射不存在时,
    cache.get(key,func)
    才会插入future(过期或收集的待清除项被视为不存在)。这只会在将来返回到缓存的过程中阻塞,如果所有工作都在将来打包,那么这应该是便宜的。任何其他请求都将等待键->未来映射建立,并在相同的将来返回,直到该映射被删除(失败或收回)


    基于此习惯用法,可以将
    AsyncCache
    的通用适配器写入Reactor。

    您可能需要一个咖啡因异步缓存来使用Mono。InvalidateUndefined的措辞主要是为了实现灵活性,它当前阻止了挂起的加载上的特定删除,但由于迭代器隐藏了这些条目,因此无法清除。异步缓存删除只是为了删除未来,而不是它的结果。您介意在这里再举一些例子吗?您可以试试。它有助于缓存原始Mono并调用外部服务一次。我还不清楚我发明的缓存失效的问题是什么。它帮助我拥有唯一的上下文执行,这意味着一次只能执行一个上下文(另一个上下文将在没有任何互斥锁的情况下反应性地等待结束)。但自述文件还没有准备好,只提供代码注释。