简体   繁体   English

Webflux 订阅者

[英]Webflux subscriber

I'm currently facing an issue about saving on redis inside a switchIfEmpty function.我目前面临一个关于在 switchIfEmpty function 中保存 redis 的问题。 Probably is related to the fact I'm pretty new in Reactive Programming and unfortunately I struggling to find proper examples about it.可能与我在反应式编程中相当新的事实有关,不幸的是我很难找到有关它的适当示例。

However I've been able to solve it but I'm pretty sure there's a better way to do it.但是我已经能够解决它,但我很确定有更好的方法来解决它。 Here is what I wrote now:这是我现在写的:

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;
               }
    ));
}

any clue or suggestion of better ways to do it?有什么更好的方法的线索或建议吗? Thank you in advance for the help!预先感谢您的帮助!

There's a few things that aren't great from a best practice perspective there, and this probably isn't doing exactly what you think it's doing:从最佳实践的角度来看,有一些事情不是很好,这可能并没有完全按照你的想法做:

  • You're subscribing yourself, which is usually a clear sign something is wrong.您正在订阅自己,这通常是出现问题的明显迹象。 Special cases aside, subscribing should generally be left to the framework.除了特殊情况,订阅通常应该留给框架。 This is also the reason you need the Mono.defer() - usually, nothing would happen until the framework subscribed to your publisher at the correct time, whereas you're managing the subscription lifecycle for that publisher yourself.这也是您需要Mono.defer()的原因 - 通常,在框架在正确的时间订阅您的发布者之前什么都不会发生,而您自己管理该发布者的订阅生命周期。
  • The framework will still subscribe to your inner publisher there, it's just that the Mono that you're returning doesn't do anything with its result.该框架仍将在那里订阅您的内部发布者,只是您返回的Mono对其结果没有任何作用。 So you'll likely be calling contentService.getContentByIdAndType() twice, not just once - once when you subscribe, and once when the framework subscribes.因此,您可能会调用contentService.getContentByIdAndType()两次,而不仅仅是一次 - 一次是在您订阅时,一次是在框架订阅时。
  • Subscribing on an inner publisher like this creates a "fire and forget" type model, which means when your reactive method returns you've got no clue if redis has actually saved it yet, which could cause issues if you then come to rely on that result later.订阅这样的内部发布者会创建一个“即发即弃”类型 model,这意味着当您的反应方法返回时,您不知道 redis 是否实际上已经保存了它,如果您依赖它,这可能会导致问题以后的结果。
  • Unrelated to the above, but contentId is already a string, you don't need to call toString() on it:-)与上面无关,但是contentId已经是一个字符串,你不需要在它上面调用toString() :-)

Instead, you might consider making use of delayUntil inside your switchIfEmpty() block - this will allow you to save the value to redis if the response code is ok, delay until this has happened, and keep the original value when done.相反,您可能会考虑在您的switchIfEmpty()块中使用delayUntil - 如果响应代码正常,这将允许您将值保存到 redis,延迟直到发生这种情况,并在完成后保留原始值。 The code might look something like this (difficult to say if this is exactly correct without a complete example, but it should give you the idea):代码可能看起来像这样(如果没有完整的示例,很难说这是否完全正确,但它应该给你一个想法):

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