Java 线程安全反应式缓存
一旦我不得不开始打多个外部电话,我总是被被动的思维方式所困扰。现在我的任务是使用Reactor和Webflux实现以下场景Java 线程安全反应式缓存,java,rx-java,rx-java2,project-reactor,Java,Rx Java,Rx Java2,Project Reactor,一旦我不得不开始打多个外部电话,我总是被被动的思维方式所困扰。现在我的任务是使用Reactor和Webflux实现以下场景 从缓存中获取令牌 找不到令牌 来自身份验证系统新令牌的请求 在缓存中持久化令牌 找到代币 使用令牌请求外部系统 外部系统响应2xx 返回响应 外部系统响应403 使缓存中的令牌无效 从顶部重试整个流 到目前为止,我只举了一个关于如何使用缓存的示例: public static Mono<ServerResponse> doRequest(
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并调用外部服务一次。我还不清楚我发明的缓存失效的问题是什么。它帮助我拥有唯一的上下文执行,这意味着一次只能执行一个上下文(另一个上下文将在没有任何互斥锁的情况下反应性地等待结束)。但自述文件还没有准备好,只提供代码注释。