近期项目用到了缓存,该项目的业务缓存使用规则是先查找本地缓存,本地缓存没有再从redis缓存里面查找,redis缓存没有,再远程接口调用查找。其代码如下:
public LoadingCache<String,String> domainNameCache = Caffeine.newBuilder()
.expireAfterAccess(Constants.DOMAIN_LOCAL_CACHE_EXPIRE_TIME, TimeUnit.MINUTES)
// .refreshAfterWrite(30, TimeUnit.SECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String domainId) throws Exception {
logger.info("load domainName for domainId:[{}]",domainId);
String domainName = getDomainNameFromRedis(domainId);
if(StringUtil.isBlank(domainName)){
domainName = getDomainNameFromRemote(domainId);
if(StringUtil.isNotBlank(domainName)){
redisUtil.setAsync(domainId,domainName,Constants.DOMAIN_REDIS_CACHE_EXPIRE_TIME,TimeUnit.DAYS);
}
}
return domainName;
}
});
上述的代码还是比较清晰易懂,但是实际操作过程发现,当redis缓存中没查到数据,竟然也没有去调用远程接口取数据。曾经一度怀疑是不是caffeine和redis不能混用,但是显然是想多了,哈哈。后面查看了getDomainNameFromRedis(domainId)这个方法实现。
该代码实现如下:
//redisUtil.get(domainId) 返回值是Object类型
private String getDomainNameFromRedis(String domainId){
String domainName = String.valueOf(redisUtil.get(domainId));
return domainName;
}
如果单纯调用这个方法完全没问题,但为啥和上述业务缓存搭配就出现问题,再回忆下上述业务缓存实现里面有这么一行逻辑
if(StringUtil.isBlank(domainName)){
domainName = getDomainNameFromRemote(domainId);
}
当domainName为空时,再调用远程接口,而事实上domainName永远都不会是空的,为啥呢,我们查看一下String.value()的源码看一下就一目了然,其源代码如下
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
当String.value()接收到一个空字符时,它返回“null”字符串,自此真相大白
其实这个问题如果细心debug一下,就很容易排查出来,但是有时候事情一多,脑子一抽,就没注意null和“null”,或者有时候省事直接控制台输出结果,而控制台打印出来的都null,这下就直接懵逼。下一篇就深入介绍string一下,当然不是我科普,网上找来的,哈哈哈