简体   繁体   English

在 Spring Web 通量中执行没有人订阅的 Mono 流

[英]Executing Mono streams that no one subscribes to in Spring Web flux

I have a spring Webflux application.我有一个 spring Webflux 应用程序。 There are two important parts to this application:此应用程序有两个重要部分:

  1. A job is scheduled to run at a fixed interval.作业计划以固定的时间间隔运行。
  2. The job fetches the data from DB and stores the data in Redis.该作业从 DB 中获取数据并将数据存储在 Redis 中。
void run() {
  redisAdapter.getTtl()
    .doOnError(RefreshExternalCache::logError)
    .switchIfEmpty(Mono.defer(() -> {
        log.debug(">> RefreshExternalCache > refreshExternalCacheIfNeeded => Remaining TTL could not be retrieved. Cache does not exist. " +
                "Trying to create the cache.");
        return Mono.just(Duration.ofSeconds(0));
    }))
    .subscribe(remainingTtl -> {
        log.debug(">> RefreshExternalCache > refreshExternalCacheIfNeeded => original ttl for the cache: {} | ttl for cache in seconds = {} | ttl for cache in minutes = {}",
                remainingTtl, remainingTtl.getSeconds(), remainingTtl.toMinutes());

        if (isExternalCacheRefreshNeeded(remainingTtl, offerServiceProperties.getExternalCacheExpiration(), offerServiceProperties.getExternalCacheRefreshPeriod())) {
            log.debug(">> RefreshExternalCache > refreshExternalCacheIfNeeded => external cache is up-to-date, skipping refresh");
        } else {
            log.debug(">> RefreshExternalCache > refreshExternalCacheIfNeeded => external cache is outdated, updating the external cache");
            offerService.refreshExternalCache();
        }
    });
}

This basically calls another method called refreshExternalCache() , the implementation below:这基本上调用了另一个名为refreshExternalCache()的方法,实现如下:

public void refreshExternalCache() {
    fetchOffersFromSource()
        .doOnNext(offerData -> {
            log.debug(LOG_REFRESH_CACHE + "Updating local offer cache with data from source");
            localCache.put(OFFER_DATA_KEY, offerData);
            storeOffersInExternalCache(offerData, offerServiceProperties.getExternalCacheExpiration());
        })
        .doOnSuccess(offerData -> meterRegistry.counter(METRIC_EXTERNAL_CACHE_REFRESH_COUNTER, TAG_OUTCOME, SUCCESS).increment())
        .doOnError(sourceThrowable -> {
            log.debug(LOG_REFRESH_CACHE + "Error while refreshing external cache {}", sourceThrowable.getMessage());
            meterRegistry.counter(METRIC_EXTERNAL_CACHE_REFRESH_COUNTER, TAG_OUTCOME, FAILURE).increment();
        }).subscribe();
}

Also, in the above method, you can see a call to storeOffersInExternalCache此外,在上述方法中,您可以看到对storeOffersInExternalCache的调用

public void storeOffersInExternalCache(OfferData offerData, Duration ttl) {
    log.info(LOG_STORING_OFFER_DATA + "Storing the offer data in external cache...");
    redisAdapter.storeOffers(offerData, ttl);
}
public void storeOffers(OfferData offerData, Duration ttl) {
    Mono.fromRunnable(() -> redisClient.storeSerializedOffers(serializeFromDomain(offerData), ttl)
        .doOnNext(status -> {
            if (Boolean.TRUE.equals(status)) {
                log.info(LOG_STORE_OFFERS + "Data stored in redis.");
                meterRegistry.counter(METRIC_REDIS_STORE_OFFERS, TAG_OUTCOME, SUCCESS).increment();
            } else {
                log.error(LOG_STORE_OFFERS + "Unable to store data in redis.");
                meterRegistry.counter(METRIC_REDIS_STORE_OFFERS, TAG_OUTCOME, FAILURE).increment();
            }
        }).retryWhen(Retry.backoff(redisRetryProperties.getMaxAttempts(), redisRetryProperties.getWaitDuration()).jitter(redisRetryProperties.getBackoffJitter()))
        .doOnError(throwable -> {
            meterRegistry.counter(METRIC_REDIS_STORE_OFFERS, TAG_OUTCOME, FAILURE).increment();
            log.error(LOG_STORE_OFFERS + "Unable to store data in redis. Error: [{}]", throwable.getMessage());
        })).subscribeOn(Schedulers.boundedElastic());
}

Redis Client Redis 客户端

@Slf4j
@Component
public class RedisClient {
    private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;
    private final ReactiveValueOperations<String, String> reactiveValueOps;

    public RedisClient(@Qualifier("reactiveRedisTemplate") ReactiveRedisTemplate<String, String> reactiveRedisTemplate) {
        this.reactiveRedisTemplate = reactiveRedisTemplate;
        this.reactiveValueOps = reactiveRedisTemplate.opsForValue();
    }

    Mono<Optional<String>> fetchSerializedOffers() {
        return reactiveValueOps.get(OFFER_DATA_KEY).map(Optional::ofNullable);
    }

    Mono<Boolean> storeSerializedOffers(String serializedOffers, Duration ttl) {
        return reactiveValueOps.set(OFFER_DATA_KEY, serializedOffers, ttl);
    }

    Mono<Duration> getTtl() {
        return reactiveRedisTemplate.getExpire(OFFER_DATA_KEY);
    }
}

Now my concerns are:现在我担心的是:

  1. If I do not call the subscribe method on these Mono streams, these methods are not even executed.如果我不对这些 Mono 流调用subscribe方法,这些方法甚至都不会执行。 This is fair as they won't execute until someone subscribes to them.这是公平的,因为在有人订阅它们之前它们不会执行。
  2. As I understand it correctly, subscribe is a blocking call.据我正确理解, subscribe是一个阻塞调用。 This defeats the whole purpose of Reactive programming.这违背了反应式编程的全部目的。 Isn't it?不是吗?
  3. I looked for several ways to make this work, one of them has been shown above.我寻找了几种方法来完成这项工作,其中一种已在上面显示。 I tried calling one of the methods in Mono.fromRunnable but this also is not a very good approach.我尝试调用Mono.fromRunnable中的一种方法,但这也不是一个很好的方法。 (read it on another thread in StackOverflow). (在 StackOverflow 的另一个线程上阅读它)。

So, is the approach that I am taking above not correct?那么,我上面采取的方法不正确吗? How do we execute the Mono streams that no one subscribes to?我们如何执行没有人订阅的 Mono 流?

Answering your concern number 2 (which seems to be the only real doubt in your question).回答您的第 2 个问题(这似乎是您问题中唯一真正的疑问)。 Not really.并不真地。 block() ( https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#block-- ) is the one that subscribes to a Mono or Flux and waits indefinitely until a next signal is received. block()https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#block-- )是订阅MonoFlux并无限期等待直到下一个接收到信号。 On the other hand subscribe() ( https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#subscribe-- ) subscribes to a Mono or Flux but it doesn't block and instead reacts when an element is emitted.另一方面subscribe() ( https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#subscribe-- ) 订阅了MonoFlux但它不会阻塞而是在发射元素时做出反应。

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

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