Spring 引导缓存预热
Spring boot cache warm up
我需要对 spring 缓存功能有一些基本的了解。我想构建一个 cache warmer 和 resyncer as scheduled bean,以便在发生变化时按需更新缓存。
比方说,我有一个 AccountClient
和一个 getAccount(String id)
,它从一个非常慢的 API 中获取一个 Account
。所以基本上我可以做到
@Cachable(cacheNames = "account", key = "#id", sync = true)
public Account getAccount(String id) {
//...
}
一切正常。现在我想预热缓存,此外,我得到了一个 getNewOrChangedAccounts()
,它从我的慢速数据存储中检索已更改帐户的 ID。
所以这是我的方法:
public class AccountCacheManager {
//...
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
//delete old entry
evictAccount(account.getId());
//store new entry
putAccount(account.getId());
});
log.info("finished resync");
}
@CacheEvict(cacheNames = "account", key = "#id")
public void evictAccount(String id) {
log.debug("evicting account {}", id);
}
@CachePut(cacheNames = "account", key = "#id")
public void putAccount(String id) {
log.debug("storing account {}", id);
accountService.getAccount(id);
}
}
所以我可以证明,这个过程开始并做了一些事情。然而,当我点击我的 API 时,我看到第一个点击进入慢速后端,即使我的同步遍历了后端的所有条目。
我觉得我误解了 spring 缓存的一些细节 API,那么我该如何实现呢?
来自 documentation 对于 @CachePut
:
In contrast to the @Cacheable annotation, this annotation does not
cause the advised method to be skipped. Rather, it always causes the
method to be invoked and its result to be stored in the associated
cache.
所以你的方法用 @CachePut
注释应该 return 要缓存的数据:
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
另见这个问题:
在幕后,当在 class 上声明 @Cacheable
或 @CachePut
注释时,Spring 创建一个环绕 bean 的代理 class实例从 class 创建。
这个抽象缓存层将仅可用于来自class之外的任何方法调用。
解决上述问题的简单方法是将 cron 方法分离到不同的 class 并通过注入的 bean 调用缓存的方法。
@Component
public class AccountCacheManager {
//...
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
}
@Component
public class CacheWarmupManager {
// bean injected here is actually a proxy that wrapped AccountCacheManager with cache implementation
@Autowired private AccountCacheManager accountManager;
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
// force update Cache with new Entry
accountManager.putAccount(account.getId());
});
log.info("finished resync");
}
}
注:
您的缓存预热 cron 方法不起作用的原因是因为该方法与缓存方法 相同 class。因此,当 cron 触发时,它会直接调用 putAccount
和 evictAccount
方法 ,而不通过提供 class 的代理缓存。
我需要对 spring 缓存功能有一些基本的了解。我想构建一个 cache warmer 和 resyncer as scheduled bean,以便在发生变化时按需更新缓存。
比方说,我有一个 AccountClient
和一个 getAccount(String id)
,它从一个非常慢的 API 中获取一个 Account
。所以基本上我可以做到
@Cachable(cacheNames = "account", key = "#id", sync = true)
public Account getAccount(String id) {
//...
}
一切正常。现在我想预热缓存,此外,我得到了一个 getNewOrChangedAccounts()
,它从我的慢速数据存储中检索已更改帐户的 ID。
所以这是我的方法:
public class AccountCacheManager {
//...
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
//delete old entry
evictAccount(account.getId());
//store new entry
putAccount(account.getId());
});
log.info("finished resync");
}
@CacheEvict(cacheNames = "account", key = "#id")
public void evictAccount(String id) {
log.debug("evicting account {}", id);
}
@CachePut(cacheNames = "account", key = "#id")
public void putAccount(String id) {
log.debug("storing account {}", id);
accountService.getAccount(id);
}
}
所以我可以证明,这个过程开始并做了一些事情。然而,当我点击我的 API 时,我看到第一个点击进入慢速后端,即使我的同步遍历了后端的所有条目。
我觉得我误解了 spring 缓存的一些细节 API,那么我该如何实现呢?
来自 documentation 对于 @CachePut
:
In contrast to the @Cacheable annotation, this annotation does not cause the advised method to be skipped. Rather, it always causes the method to be invoked and its result to be stored in the associated cache.
所以你的方法用 @CachePut
注释应该 return 要缓存的数据:
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
另见这个问题:
在幕后,当在 class 上声明 @Cacheable
或 @CachePut
注释时,Spring 创建一个环绕 bean 的代理 class实例从 class 创建。
这个抽象缓存层将仅可用于来自class之外的任何方法调用。
解决上述问题的简单方法是将 cron 方法分离到不同的 class 并通过注入的 bean 调用缓存的方法。
@Component
public class AccountCacheManager {
//...
@CachePut(cacheNames = "account", key = "#id")
public Account putAccount(String id) {
log.debug("storing account {}", id);
return accountService.getAccount(id);
}
}
@Component
public class CacheWarmupManager {
// bean injected here is actually a proxy that wrapped AccountCacheManager with cache implementation
@Autowired private AccountCacheManager accountManager;
@Scheduled(initialDelay = 3000L, fixedRate = 10000L)
public void sync() {
List<Account> modifiedAccounts = accountClient.getNewOrChangedAccounts();
modifiedAccounts.getAccounts().parallelStream()
.forEach(account -> {
// force update Cache with new Entry
accountManager.putAccount(account.getId());
});
log.info("finished resync");
}
}
注:
您的缓存预热 cron 方法不起作用的原因是因为该方法与缓存方法 相同 class。因此,当 cron 触发时,它会直接调用 putAccount
和 evictAccount
方法 ,而不通过提供 class 的代理缓存。