关于主题1:
只是一个旁注:请不要混淆到期和驱逐。过期意味着该条目可能不再由缓存返回,并且可能发生在指定的时间点或持续时间之后。驱逐是释放资源的动作,条目从缓存中删除。到期后,驱逐可能在同一时间或之后发生。
所有常见的缓存产品都不支持精确的,也就是“时间点”,到期。我们的应用程序中经常需要这个用例,所以我在cache2k 上花费了一些精力来支持它。
这是 cache2k 的蓝图:
static class MetricsEntry {
long nextUpdate;
List<Metrics> metrics;
}
static class MyEntryExpiryCalculator implements EntryExpiryCalculator<Integer, MetricsEntry> {
@Override
public long calculateExpiryTime(Integer _key, MetricsEntry _value, long _fetchTime, CacheEntry _oldEntry) {
return _value.nextUpdate;
}
}
Cache createTheCache() {
Cache<Integer, MetricsEntry> cache =
CacheBuilder.newCache(Integer.class, MetricsEntry.class)
.sharpExpiry(true)
.entryExpiryCalculator(new MyEntryExpiryCalculator())
.source(new MySource())
.build();
return cache;
}
如果您在度量对象中有时间参考,则可以使用它,并且可以省略额外的条目类。 sharpExpiry(true) 指示 cache2k 的确切到期时间。如果您忽略它,到期时间可能会延迟几毫秒,但访问时间会稍微快一些。
关于主题 2:
直接的方法是使用间隔分钟作为缓存键。
这里是一个缓存源(又名缓存加载器),它严格返回上一个区间的指标:
static class MySource implements CacheSource<Integer, MetricsEntry> {
@Override
public MetricsEntry get(Integer interval) {
MetricsEntry e = new MetricsEntry();
boolean crossedIntervalEnd;
do {
long now = System.currentTimeMillis();
long intervalMillis = interval * 1000 * 60;
long startOfInterval = now % (intervalMillis);
e.metrics = calculateMetrics(startOfInterval, interval);
e.nextUpdate = startOfInterval + intervalMillis;
now = System.currentTimeMillis();
crossedIntervalEnd = now >= e.nextUpdate;
} while (crossedIntervalEnd);
return e;
}
}
如果您在 10:07 发出请求,这将返回 10:00-10:05 的指标。
如果你只是想立即计算过去区间的指标,那就更简单了:
static class MySource implements CacheSource<Integer, MetricsEntry> {
@Override
public MetricsEntry get(Integer interval) {
MetricsEntry e = new MetricsEntry();
long intervalMillis = interval * 1000 * 60;
long startOfInterval = System.currentTimeMillis();
e.metrics = calculateMetrics(startOfInterval, interval);
e.nextUpdate = startOfInterval + intervalMillis;
return e;
}
}
使用缓存源优于put()。 cache2k 是阻塞的,所以如果一个指标有多个请求,只开始一个指标计算。
如果您不需要精确到毫秒的过期时间,您也可以使用其他缓存。您需要做的是将计算指标所需的时间存储在缓存值中,然后相应地更正到期持续时间。
祝你好运!