繁体   English   中英

Webflux 订阅者

[英]Webflux subscriber

我目前面临一个关于在 switchIfEmpty function 中保存 redis 的问题。 可能与我在反应式编程中相当新的事实有关,不幸的是我很难找到有关它的适当示例。

但是我已经能够解决它,但我很确定有更好的方法来解决它。 这是我现在写的:

public Mono<ResponseEntity<BaseResponse<Content>>> getContent(final String contentId, final String contentType){
    return redisRepository.findByKeyAndId(REDIS_KEY_CONTENT, contentId.toString()).cast(Content.class)
               .map(contentDTO -> ResponseEntity.status(HttpStatus.OK.value())
                                                .body(new BaseResponse<>(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), contentDTO)))
               //here I have to defer, otherwise It would never wait for the findByKeyAndId 
               .switchIfEmpty(Mono.defer(() -> {
                   Mono<ResponseEntity<BaseResponse<Content>>> responseMono = contentService.getContentByIdAndType(contentId, contentType);
                   
                   //so far I understood I need to consume every stream I have, in order to actually carry out the task otherwise will be there waiting for a consumer.
                   //once I get what I need from the cmsService I need to put it in cache and till now this is the only way I've been able to do it
                   responseMono.filter(response -> response.getStatusCodeValue() == HttpStatus.OK.value())
                               .flatMap(contentResponse -> redisRepository.save(REDIS_KEY_CONTENT, contentId.toString(), contentResponse.getBody().getData()))
                                        .subscribe();
                   //then I return the Object I firstly retrived thru the cmsService
                   return responseMono;
               }
    ));
}

有什么更好的方法的线索或建议吗? 预先感谢您的帮助!

从最佳实践的角度来看,有一些事情不是很好,这可能并没有完全按照你的想法做:

  • 您正在订阅自己,这通常是出现问题的明显迹象。 除了特殊情况,订阅通常应该留给框架。 这也是您需要Mono.defer()的原因 - 通常,在框架在正确的时间订阅您的发布者之前什么都不会发生,而您自己管理该发布者的订阅生命周期。
  • 该框架仍将在那里订阅您的内部发布者,只是您返回的Mono对其结果没有任何作用。 因此,您可能会调用contentService.getContentByIdAndType()两次,而不仅仅是一次 - 一次是在您订阅时,一次是在框架订阅时。
  • 订阅这样的内部发布者会创建一个“即发即弃”类型 model,这意味着当您的反应方法返回时,您不知道 redis 是否实际上已经保存了它,如果您依赖它,这可能会导致问题以后的结果。
  • 与上面无关,但是contentId已经是一个字符串,你不需要在它上面调用toString() :-)

相反,您可能会考虑在您的switchIfEmpty()块中使用delayUntil - 如果响应代码正常,这将允许您将值保存到 redis,延迟直到发生这种情况,并在完成后保留原始值。 代码可能看起来像这样(如果没有完整的示例,很难说这是否完全正确,但它应该给你一个想法):

return redisRepository
        .findByKeyAndId(REDIS_KEY_CONTENT, contentId).cast(Content.class)
        .map(contentDTO -> ResponseEntity.status(HttpStatus.OK.value()).body(new BaseResponse<>(HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), contentDTO)))
        .switchIfEmpty(
                contentService.getContentByIdAndType(contentId, contentType)
                        .delayUntil(response -> response.getStatusCodeValue() == HttpStatus.OK.value() ?
                                redisRepository.save(REDIS_KEY_CONTENT, contentId, contentResponse.getBody().getData()) :
                                Mono.empty())
        );

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM