我正在使用Spring Cache@CacheEvict
&@Cacheable
目前,我每小时运行一个调度程序来清除缓存,下次调用fetchUser((时,它将从外部APi获取数据并添加到缓存中。
@Scheduled(cron = "0 0 * * * *}")
@CacheEvict(value = "some-unique-value", allEntries = true)
public void clearUserCache() {
log.info("Cache cleared");
}
@Cacheable(value = "some-unique-value", unless = "#result.isFailure()")
@Override
public Result<UserResponse> fetchUser() {
try {
UserResponse userResponse = api.fetchUserDetail();
return Result.success(userResponse);
} catch (Exception e) {
return Result.failure(INTERNAL_SERVER_ERROR);
}
}
现在我们需要的是只有当用户API调用成功时才清除缓存。有办法做到这一点吗。
现在按计划清除缓存,并假设外部API调用失败。主API将返回错误响应。在这种情况下,我应该能够使用现有的缓存本身。
如果我正确地得到了它,为什么不在检查API调用在该方法的父级是否正确后将其作为正常方法调用?
用你的代码,一些类似的东西
// we just leave scheduled here as you need it.
@Scheduled(cron = "0 0 * * * *}")
@CacheEvict(value = "some-unique-value", allEntries = true)
public void clearUserCache() {
log.info("Cache cleared");
}
@Cacheable(value = "some-unique-value", unless = "#result.isFailure()")
@Override
public Result<UserResponse> fetchUser() {
try {
UserResponse userResponse = api.fetchUserDetail();
return Result.success(userResponse);
} catch (Exception e) {
return Result.failure(INTERNAL_SERVER_ERROR);
}
}
public void parentMethod() {
Result<UserResponse> userResult = this.fetchUser();
if(userResult.isFailure()) {
this.clearUserCache();
}
}
这样,如果抛出任何Exception
,它将返回failure
状态,您可以对其进行检查。因此,缓存将每小时清除一次,或者在不工作时清除一次。
所以下一次,由于它失败了,而且没有缓存,它会再试一次。
我没有找到任何直接的实现,但通过一个解决方案,我能够做到。
用例
- 只有在触发使用用户API的下一个服务调用时,才应更新用户API响应。调度程序不应更新它。由于我们需要将来自外部系统的头信息传递给用户API
- 只有当用户API响应成功时,才必须清除缓存
步骤:
- 在调度程序中添加了一个变量,并在调度时间时将其设置为
ON
,在更新缓存时将其设为OFF
- 这个标志在
UserService
类中用于检查调度程序是否被触发 - 如果没有,请使用缓存。如果是
true
,则触发用户API调用。如果成功,请检查响应。触发CacheEvict
方法并更新Cache
示例代码:
调度配置
private boolean updateUserCache;
@Scheduled(cron = "${0 0 * * * *}") // runs every Hr
public void userScheduler() {
updateUserCache = true;
log.info("Scheduler triggered for User");
}
@CacheEvict(value = "USER_CACHE", allEntries = true)
public void clearUserCache() {
updateUserCache = false;
log.info("User cache cleared");
}
public boolean isUserCacheUpdateRequired() {
return updateUserCache;
}
用户服务
UserResponse userResponse = null;
if (schedulerConfig.isUserCacheUpdateRequired()) {
userResponse = userCache.fetchUserDetail();
if (userResponse != null) {
// clear's cache and userResponse is stored in cache automatically when getUserDetail is called below
schedulerConfig.clearUserCache();
}
}
return userCache.getUserDetail(userResponse);
用户缓存
@Cacheable(value = "USER_CACHE", key = "#root.targetClass", unless = "#result.isFailure()")
public Result<User> getUserDetail(UserResponse userResponse) {
try {
if (userResponse == null) { // handle first time trigger when cache is not available
userResponse = fetchUserDetail(); // actual API call
}
return Result.success(mapToUser(userResponse));
} catch (Exception e) {
return Result.failure("Error Response");
}
}
注意:
- Result是一个自定义的Wrapper,假设它是一个具有成功或失败属性的对象
- 我不得不将
@Cacheable
部分添加为单独的Bean,因为缓存只适用于代理对象。如果我将getUserDetail
保留在UserService
内部并直接调用,则由于代理和缓存逻辑不工作,它不会被拦截,每次都会触发API调用 - 最重要的是:这不是最好的解决方案,还有改进的余地