我已经使用CacheBuilder创建了一个缓存。 我用过ExpireAfterWrite和RefreshAfterWrite。我已覆盖cacheloader的加载和重新加载功能。实际上,在重载中,我通过创建ListenableFutureTask并将其提交给ExecutorService来调用重载。 下面是我得到的堆栈跟踪-
警告:刷新期间引发异常 [junit] com.google.common.cache.CacheLoader $ InvalidCacheLoadException: CacheLoader为键abc返回null。 [junit] com.google.common.cache.LocalCache $ Segment.getAndRecordStats(未知 资源) [junit] com.google.common.cache.LocalCache $ Segment $ 1.run(未知来源) [junit] com.google.common.util.concurrent.MoreExecutors $ DirectExecutor.execute(未知 资源) [junit]位于com.google.common.util.concurrent.ImmediateFuture.addListener(未知 资源) [junit] com.google.common.cache.LocalCache $ Segment.loadAsync(未知源) [junit]位于com.google.common.cache.LocalCache $ Segment.refresh(未知来源) [junit] com.google.common.cache.LocalCache $ Segment.scheduleRefresh(未知 资源) [junit]位于com.google.common.cache.LocalCache $ Segment.get(未知来源) com.google.common.cache.LocalCache.get上的[junit](未知来源) [junit] com.google.common.cache.LocalCache.getOrLoad(未知源)
我不知道为什么它不能刷新,也不能到达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);
}
}
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);
}
}
答案 0 :(得分:0)
所以这是在测试文件中的SimpleCacheLoader上进行的@嘲笑,由于我没有对方法进行存根,因此刷新时未执行任何操作。在这种情况下,我应该使用@spy。我把它修好了。谢谢大家的帮助。