场景:
集群项目使用访问策略使用的随机,缓存使用的redis,自定义key当传入两个参数以上时候,使用Arrays.deepHashCode来计算缓存的key。
问题:
然后出现在机器1刚访问过这个接口,redis也有缓存,访问到机器2的时候仍然走service方法,也就是没有从redis取缓存。
(从日志看是否走的缓存,在自定义key生成里面打上日志,和接口调用的service打上日志,如果走了自定义key方法两遍就是在生成缓存,如果走一遍自定义key这个方法就是取的缓存)
然后找日志,同一个接口,在访问到不同机器的时候,生成的缓存key不一样,所以导致每个机器都需要缓存一遍。
原因:生成hash key的方法为Arrays.deepHashCode看方法
public static int deepHashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a) {
int elementHash = 0;
if (element instanceof Object[])
elementHash = deepHashCode((Object[]) element);
else if (element instanceof byte[])
elementHash = hashCode((byte[]) element);
else if (element instanceof short[])
elementHash = hashCode((short[]) element);
else if (element instanceof int[])
elementHash = hashCode((int[]) element);
else if (element instanceof long[])
elementHash = hashCode((long[]) element);
else if (element instanceof char[])
elementHash = hashCode((char[]) element);
else if (element instanceof float[])
elementHash = hashCode((float[]) element);
else if (element instanceof double[])
elementHash = hashCode((double[]) element);
else if (element instanceof boolean[])
elementHash = hashCode((boolean[]) element);
else if (element != null)
elementHash = element.hashCode();
result = 31 * result + elementHash;
}
return result;
}
查看
elementHash = element.hashCode();
public native int hashCode();
这是c写的,看看上面注释。
address of the object into an integer, but this implementation
说会根据数据存储的地址生成,所以数据在不同机器上存的位置不一样,导致hash生成的key不一致。
结论:
集群情况下生成hash值想要保持一致,需要避免使用public native int hashCode(); 这个受保存地址影响的hashcode方法。
比如String重新了hashcode,没有调用原生的hashcode,没有任何受地址影响的地方。
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
可以在集群情况下。每个机器上,相同参数的情况下,保持生成的hash key一致。
这样就可以保证,在一个机器上生成缓存,下次请求到另外一个机器,仍然可以使用这个缓存。