我需要以不同的时间间隔检查一些endpoint,因此我设置了Caffeine的缓存构建器。
this. localeWeatherCache=newBuilder().build();
这个。当前天气缓存=new Builder()。写入后到期(持续时间。小时(3))。构建();
this. weatherTruastsCache=newBuilder().expireAfterWrite(Duration.ofHour(12)).build();
在我的服务中,我调用这3个endpoint,最后我使用Mono.zip()返回我的对象和所有细节。
在我的测试过程中,我注意到climaTempoReposity. findLocaleByCityNameAndState被执行了两次,并且在当前Weather缓存到期后,它会再次调用localeendpoint,weather预测也是如此,它会再次调用locale。
为什么失败?不应该使用缓存吗?还是我做的方式不对?
任何帮助或指针都非常感谢!:)
public Mono<Weather> weatherForecastByLocation(Location location) {
Mono<ClimaTempoLocale> locale =
CacheMono.lookup(key ->
Mono.justOrEmpty(localeWeatherCache.getIfPresent(key))
.map(Signal::next), location)
.onCacheMissResume(() -> climaTempoRepository.findLocaleByCityNameAndState(location.city(), location.state()))
.andWriteWith((key, signal) -> Mono.fromRunnable(() ->
Optional.ofNullable(signal.get())
.ifPresent(value -> localeWeatherCache.put(key, value))));
Mono<CurrentWeather> currentWeather =
CacheMono.lookup(key ->
Mono.justOrEmpty(currentWeatherCache.getIfPresent(key))
.map(Signal::next), location)
.onCacheMissResume(() -> locale.flatMap(climaTempoRepository::findCurrentWeatherByLocale)
.subscribeOn(Schedulers.elastic()))
.andWriteWith((key, signal) -> Mono.fromRunnable(() ->
Optional.ofNullable(signal.get())
.ifPresent(value -> currentWeatherCache.put(key, value))));
Mono<WeatherForecasts> weatherForecasts =
CacheMono.lookup(key ->
Mono.justOrEmpty(weatherForecastsCache.getIfPresent(key))
.map(Signal::next), location)
.onCacheMissResume(() -> locale.flatMap(climaTempoRepository::findDailyForecastByLocale)
.subscribeOn(Schedulers.elastic()))
.andWriteWith((key, signal) -> Mono.fromRunnable(() ->
Optional.ofNullable(signal.get())
.ifPresent(value -> weatherForecastsCache.put(key, value))));
return Mono.zip(currentWeather,
weatherForecasts,
(current, forecasts) ->
Weather.buildWith(builder -> {
builder.location = location;
builder.currentWeather = current;
builder.weatherForecasts = forecasts;
}));
}
AsyncLoadingCache
可以从键计算值并返回结果的CompletableFuture
。这可以转换为Mono
它的fromFuture
方法。这将确保给定键只有一次执行在进行中,而不会因为将期货存储在缓存中而阻塞。
AsyncLoadingCache<Location, ClimaTempoLocale> localeWeatherCache =
Caffeine.newBuilder().buildAsync(location ->
climaTempoRepository.findLocaleByCityNameAndState(location.city(), location.state()));
AsyncLoadingCache<ClimaTempoLocale, CurrentWeather> currentWeatherCache =
Caffeine.newBuilder().buildAsync(climaTempoRepository::findCurrentWeatherByLocale);
AsyncLoadingCache<ClimaTempoLocale, WeatherForecasts> weatherForecastsCache =
Caffeine.newBuilder().buildAsync(climaTempoRepository::findDailyForecastByLocale);
public Mono<Weather> weatherForecastByLocation(Location location) {
var locale = Mono.fromFuture(localeWeatherCache.get(location));
var currentWeather = Mono.fromFuture(locale.map(localeWeatherCache::get));
var weatherForecasts = Mono.fromFuture(locale.map(weatherForecastsCache::get));
return Mono.zip(currentWeather, weatherForecasts, (current, forecasts) ->
Weather.buildWith(builder -> {
builder.location = location;
builder.currentWeather = current;
builder.weatherForecasts = forecasts;
}));
}
如图所示https://stackoverflow.com/a/52803247/11209784ClimaTempoLocale可以计算如下:
Cache<Location, ClimaTempoLocale> weatherLocaleCache = Caffeine.newBuilder().build();
private Mono<ClimaTempoLocale> findLocale(Location location) {
Mono<ClimaTempoLocale> locale;
ClimaTempoLocale cachedLocale = weatherLocaleCache.getIfPresent(location);
if (cachedLocale != null) {
locale = Mono.just(cachedLocale);
} else {
locale = climaTempoRepository.findLocaleByCityNameAndState(location.city(), location.state())
.doOnNext(climaTempoLocale -> weatherLocaleCache.put(location, climaTempoLocale));
}
return locale;
}
一个副作用是,当并发调用导致缓存未命中时,可能会连续写入同一键。
通过这样做,依赖于ClimaTempoLocale
的调用可以以相同的方式继续:
Cache<Location, CurrentWeather> currentWeatherCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofHours(3)).build();
Cache<Location, WeatherForecasts> weatherForecastsCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofHours(12)).build();
public Mono<Weather> weatherForecastByLocation(Location location) {
Mono<ClimaTempoLocale> locale = findLocale(location);
Mono<CurrentWeather> currentWeather =
CacheMono.lookup(
key -> Mono.justOrEmpty(currentWeatherCache.getIfPresent(key))
.map(Signal::next),
location)
.onCacheMissResume(
() -> locale.flatMap(climaTempoRepository::findCurrentWeatherByLocale)
.subscribeOn(Schedulers.elastic()))
.andWriteWith(
(key, signal) -> Mono.fromRunnable(
() -> Optional.ofNullable(signal.get())
.ifPresent(value -> currentWeatherCache.put(key, value))));
Mono<WeatherForecasts> weatherForecasts =
CacheMono.lookup(
key -> Mono.justOrEmpty(weatherForecastsCache.getIfPresent(key))
.map(Signal::next),
location)
.onCacheMissResume(
() -> locale.flatMap(climaTempoRepository::findDailyForecastByLocale)
.subscribeOn(Schedulers.elastic()))
.andWriteWith(
(key, signal) -> Mono.fromRunnable(
() -> Optional.ofNullable(signal.get())
.ifPresent(value -> weatherForecastsCache.put(key, value))));
return Mono.zip(currentWeather,
weatherForecasts,
(current, forecasts) ->
Weather.buildWith(builder -> {
builder.location = location;
builder.currentWeather = current;
builder.weatherForecasts = forecasts;
}));
}