我正在使用Caffeine v2.8.5,并且我想基于以下内容创建一个变量到期的缓存:
首先出现的内容应触发该条目的删除。
缓存将是三层值解析的一部分:
Redis用作全局缓存,因此多个应用程序/实例可以共享缓存的数据,但是这种解决方法经常发生,以致不能用于每个请求,因此需要另一个缓存层。 / em>
根据请求时间,请求的数据具有不同的TTL。因此,尽管当我们请求REST API时到期时间可能是固定的,并且在Redis中设置了到期时间,但是在Caffeine中该时间将是动态的,因为到期时间基于Redis Key的剩余TTL。
在Caffeine缓存的CacheLoader中已经解决了情况(2)和(3)(我在直通模式下使用了缓存)。为了控制我已经发现的到期时间,我将不得不使用advanced Expiry API,并且还研究了类似的问题,例如(Specify expiry for an Entry)和(Expire cached values after creation time)。所以我想出了一个包装对象,像这样:
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Instant;
@Value
public class ExpiringValue<ValueType> {
@Nullable
private final ValueType value;
@NotNull
private final Instant validUntil;
}
和这样的Expiry:
import com.github.benmanes.caffeine.cache.Expiry;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
import java.time.Instant;
public final class ValueBasedExpiry<KeyType, ValueType extends ExpiringValue<?>> implements Expiry<KeyType, ValueType> {
@Override
public long expireAfterCreate(
@NotNull final KeyType key,
@NotNull final ValueType value,
final long currentTime
) {
return Duration.between(Instant.now(), value.getValidUntil()).toNanos();
}
@Override
public long expireAfterUpdate(
@NotNull final KeyType key,
@NotNull final ValueType value,
final long currentTime,
final long currentDuration
) {
return currentDuration;
}
@Override
public long expireAfterRead(
@NotNull final KeyType key,
@NotNull final ValueType value,
final long currentTime,
final long currentDuration
) {
return currentDuration;
}
}
用例的不同之处在于,我想根据值的最后一次访问获得第二个到期条件。因此,如果一个小时都没有请求,我想尽早删除该条目。而且,如果它经常访问,它将在TTL达到零后最终被删除。
我将如何实施第二个条件?我不知道上次访问条目的方式。接口似乎没有提供这样的值。我还研究了this question。是否会根据调度程序存储段定期对方法进行调用/重新评估,是否正确?
答案 0 :(得分:1)
我对Expiries的工作方式有一个很大的误解,就是我认为,到期方法会定期触发并重新评估。我正在回答我自己的问题,以防有人可能从他们的研究中获得相同的印象。
仅在执行了相应方法名称的操作后,才会调用Expiry中的方法(因此仅更新值)。因此,例如,expireAfterRead(K, V, long, long)
仅在每次在缓存中读取此键值映射时才会调用。
因此,如果在创建映射后将不再有任何动作(无读取或更新),则仅会调用expireAfterCreate(K, V, long)
方法一次。这就是为什么所有方法都应始终返回剩余持续时间的原因,而不必考虑例如上一次读取条目的最后一次时间,因为那一刻就存在了(如Instant.now()
),则调用expireAfterRead(K, V, long, long)
。
正如@BenManes在评论中指出的那样,我最初的问题的正确解决方案正在返回
Math.min(TimeUnit.HOURS.toNanos(1), Duration.between(Instant.now(), value.getValidUntil()).toNanos())
所有三种到期日方法。
并在帖子中回答我的其他两个问题:
如何获取上次访问条目的时间?
在Instant.now()
方法中调用(例如)expireAfterRead(K, V, long, long)
。如果您还想在外部或其他expire-method中使用该值,则始终可以选择将此值存储在带有易变字段的ExpiringValue中。
是否正确,将根据调度程序存储桶定期对这些方法进行调用/重新评估,以确认条目已被分类? 否。如上所述,仅当执行了相应的操作后,才会调用Expiry中的方法。这些方法不会定期触发或重新评估。