springboot之專案叢集生成hash的key導致快取不一致。
阿新 • • 發佈:2018-11-09
場景:
叢集專案使用訪問策略使用的隨機,快取使用的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一致。
這樣就可以保證,在一個機器上生成快取,下次請求到另外一個機器,仍然可以使用這個快取。