我使用 CacheBuilder 创建了一个缓存。我使用过ExpireAfterWrite 和RefreshAfterWrite。我已经覆盖了缓存加载器加载和重新加载功能。实际上,在重新加载时,我通过创建 ListenableFutureTask 并将其提交给 ExecutorService 来调用加载。下面是我得到的堆栈跟踪 -
警告:刷新期间引发异常 [junit] com.google.common.cache.CacheLoader$InvalidCacheLoadException:CacheLoader 为键 abc 返回 null。[junit] at com.google.common.cache.LocalCache$Segment.getAndRecordStats(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment$1.run(Unknown Source) [junit] at com.google .common.util.concurrent.MoreExecutors$DirectExecutor.execute(Unknown Source) [junit] at com.google.common.util.concurrent.ImmediateFuture.addListener(Unknown Source) [junit] at com.google.common.cache.LocalCache $Segment.loadAsync(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment.refresh(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment.scheduleRefresh(Unknown Source) [junit] 在 com.google.common。
我不知道为什么它不能刷新,它也不能到达 cacheloader.load 。它之前返回null。我很确定我没有在加载函数中返回 null。
示例代码 -
public class SampleCacheLoader extends CacheLoader<String, Customer> {
private final DatabaseClient databaseClient;
private static final ExecutorService ex = Executors.newSingleThreadScheduledExecutor();
@Inject
public SampleCacheLoader(final DatabaseClient databaseClient) {
this.databaseClient = databaseClient;
}
@Override
public Customer load(final String customerId) throws Exception {
Customer customer = databaseClient.getCustomer(customerId);
if (customer == null) {
throw new Exception("Customer is null");
}
return customer;
}
@Override
public ListenableFuture<Customer> reload(final String customerId,
final Customer prevCustomer)
throws Exception {
ListenableFutureTask<Customer> task = ListenableFutureTask
.create(new Callable<Customer>() {
@Override
public Customer call() {
try {
// try to get a new value
load(customerId);
} catch (Throwable e) {
// or return the old one in the event of failure
return prevCustomer;
}
}
});
// run in the background so that concurrent get() requests still return values.
ex.execute(task);
return task;
}
}
public class SampleCache {
public LoadingCache<String, Customer> sampleCache;
@Inject
public SampleCache(final SampleCacheLoader sampleCacheLoader,
final int cacheMaxSize,
final int cacheExpireTime,
final int cacheRefreshTime,
final int concurrencryLevel,
final Ticker ticker) {
this.cache = CacheBuilder.newBuilder()
.maximumSize(cacheMaxSize)
.expireAfterWrite(cacheExpireTime, TimeUnit.MINUTES)
.refreshAfterWrite(cacheRefreshTime, TimeUnit.MINUTES)
.concurrencyLevel(concurrencryLevel)
.ticker(ticker)
.build(sampleCacheLoader);
}
public Optional<Customer> get(final String customerId) {
try {
Customer customer = cache.get(customerId);
return Optional.of(customer);
} catch (ExecutionException e) {
log.warn(String.format("failed to get customer from cache (customerId=%s)", customerId));
log.warn(e.getMessage());
}
return Optional.empty();
}
/**
* Size of customer cache.
* @return size of customer cache.
*/
public long size() {
return cache.size();
}
}
public class Customer {
private String name;
}
public class SampleCacheTest extends TestCase {
SampleCache SampleCache;
SampleCacheLoader SampleCacheLoader;
// FakeTicker to test cache expiry.
FakeTicker ft;
// Max size of cache
final int CACHE_MAX_SIZE = 1000;
// CACHE_EXPIRE_TIME is in minutes.
final int CACHE_EXPIRE_TIME = 1000;
// CACHE_REFRESH_TIME is in minutes.
final int CACHE_REFRESH_TIME = 3;
// CACHE_CONCURRENCY_LEVEL.
final int CACHE_CONCURRENCY_LEVEL = 10;
// Resource arn
final Static String CUSTOMER_ID =
"abc";
@Before
public void setUp() throws Exception {
// FaceTicker provided by google source code.
ft = new FakeTicker();
SampleCacheLoader sampleCacheLoader = new sampleCacheLoader(new DatabaseClient());
SampleCache = new SampleCache(sampleCacheLoader,
CACHE_MAX_SIZE,
CACHE_EXPIRE_TIME,
CACHE_REFRESH_TIME,
CACHE_CONCURRENCY_LEVEL,
ft);
}
@Test
public void testCacheRefreshTime() throws Exception {
Optional<Customer> customer1 = SampleCache.get(CUSTOMER_ID);
assertTrue(customer1.isPresent());
assertNotNull(customer1.get());
// Advancing time by 1 minutes and retrieve it from cache
// So that it won't expire. Gets the old entry.
advanceTimeForCache(1);
Optional<Customer> customer2 = SampleCache.get(CUSTOMER_ID);
assertTrue(customer2.isPresent());
assertNotNull(customer2.get());
// After this any get call for CUSTOMER_ID
// should initiate the refresh and Load.
// new value from db.
advanceTimeForCache(4);
// This is the place where I get CacheInvalidateStateException
Optional<Customer> customer3 = SampleCache.get(CUSTOMER_ID);
}
}