一、问题描述
在yudao的多租户系统中,有个 tenantFrameworkService 可以可以缓存租户id,系统中由于是多级租户,所以有根据租户id获所有子租户的需求,我们就在这个 tenantFrameworkService 下加了 getTenantChildren(Long tenantId) 方法来获取所有的子租户。
private final LoadingCache<Long, List<Long>> getAllTenantIdsForChildCache = CacheUtils.buildAsyncReloadingCache(
Duration.ofMinutes(1L), // 过期时间 1 分钟
new CacheLoader<Long, List<Long>>() {
@Override
public List<Long> load(Long tenantId) throws Exception {
CommonResult<List<Long>> result = tenantApi.getALLTenantIdsForChild(tenantId);
if (result.isSuccess()) {
log.info("[getALLTenantIdsForChild] 缓存命中,tenantId={},{}", tenantId, result.getCheckedData());
return result.getCheckedData();
} else {
throw new Exception(result.getMsg());
}
}
});
@Override
@SneakyThrows
public List<Long> getTenantChildren(Long tenantId) {
return getAllTenantIdsForChildCache.get(tenantId);
}
但是问题是这边查询了两个租户的所有子租户,然后使用
tenantIds.addAll(yhbzTenantIds);
把两个list合在一起了,奇怪的问题出现了,由于缓存有效期是1分钟 ,在执行一次定时任务,一分钟内再次执行的话,获取到的子租户id就会异常,会多出 yhbzTenantIds 对应的租户id。如果一分钟内执行三次的话,就会多两次 yhbzTenantIds 。
二、问题分析
由于缓存存在一分钟,且只在一分钟之内执行会出现此问题,那么基本可以判断出来是在 tenantIds.addAll(yhbzTenantIds); 这个 方法执行后,将执行后的 tenantIds 又保存进缓存里了,那么问题是什么时候存进缓存的呢?或者说是怎么更新缓存的?
查看 代码,具体的保存缓存是在 CacheUtils.buildAsyncReloadingCache 这个方法,存到 getAllTenantIdsForChildCache 这个 LoadingCache 里,LoadingCache 是以 K,V 形式保存的,经过下面的测试发现,第一次和第二次从 LoadingCache 获取到的同一个key对应的 value 是一样的,也就是保存在 堆内存的指针是没变的,所以获取到的 List 变化后,一分钟内再获取还是变化后的。
三、处理方法
其实这种处理很简单,新建个list将两个list添加进来即可
List<Long> tenantIds = new ArrayList<>();
List<Long> hgTenantIds = tenantFrameworkService.getTenantChildren(constantsTenant.HG_TENANTID);
List<Long> yhbzTenantIds = tenantFrameworkService.getTenantChildren(constantsTenant.YHBZ_TENANTID);
tenantIds.addAll(hgTenantIds);
tenantIds.addAll(yhbzTenantIds);