17章中我們講解了整個(gè)HiveMQ的Cluster的原理以及實(shí)現(xiàn)方式,值得一提的當(dāng)然是數(shù)據(jù)的Replicate,以及當(dāng)Replicate數(shù)據(jù)與本地?cái)?shù)據(jù)存在沖突時(shí),HiveMQ是如何實(shí)現(xiàn)的。
Replicate
在每一條被 running node持久化的數(shù)據(jù)都會(huì)使用Primary環(huán) Replicate。
當(dāng)node從JOINING狀態(tài)變更為RUNNING狀態(tài)前都會(huì)使用Primary環(huán) Replicate。
當(dāng)node變更為MERGE_MINORITY成功后都會(huì)使用Primary環(huán) Replicate。
當(dāng)node變更為MERGE_MINORITY成功后都會(huì)使用Minority環(huán) Replicate。
VectorClock
幾乎所有做Cluster不可避免的就是需要解決沖突,各種解法比較多,其中VectorClock做法比較流行,下面我們看看HiveMQ如何實(shí)現(xiàn)即可,具體使用VectorClock的原因、以及原理我們就不過多描述了。
注意:VectorClocks上的成員變量為Map<String, VectorClock>,
貌似是idea uml插件顯示問題。
VectorClock持有vectors的一個(gè)node與vector對(duì)應(yīng)關(guān)系,提供遞增、合并、比較這幾種功能/服務(wù),以記錄一個(gè)key在一個(gè)node上的Vector。
VectorClocks持有每一個(gè)key的VectorClock,為每一個(gè)key提供添加、刪除、get等方法/服務(wù), 通過VectorClocks就可以獲得到每個(gè)key的完整的VectorClock。
在node獲得到Replicate要求時(shí),當(dāng)本地?cái)?shù)據(jù)與備份數(shù)據(jù)存在沖突時(shí),就會(huì)使用VectorClock來進(jìn)行解決沖突。
在每個(gè)ClusterPersistence中都會(huì)持有一個(gè)VectorClocks用以解決沖突。
示例
下面我們就列舉一段ClientSessionClusterPersistenceImpl處理Replica請(qǐng)求時(shí),當(dāng)存在沖突解決沖突的代碼。
public ListenableFuture<Void> handleReplica(@NotNull String clientId, @NotNull ClientSession clientSession, long requestTimestamp, VectorClock requestVectorClock) {
Preconditions.checkNotNull(clientId, "Client id must not be null");
Preconditions.checkNotNull(clientSession, "Client session must not be null");
return getExecutor(clientId).add(() -> {
VectorClock localVectorClock = vectorClocks.get(clientId);
//當(dāng)請(qǐng)求的向量時(shí)鐘在比本地向量時(shí)鐘之前或者相當(dāng),則忽略本次備份
if (requestVectorClock.before(localVectorClock) ||
requestVectorClock.equals(localVectorClock)) {
return null;
}
//當(dāng)本地向量時(shí)鐘在請(qǐng)求向量時(shí)鐘之前,則直接保存即可
if (localVectorClock.before(requestVectorClock)) {
vectorClocks.put(clientId, requestVectorClock);
clientSessionLocalPersistence.persistent(clientId, clientSession, requestTimestamp);
} else {
//當(dāng)兩個(gè)向量時(shí)鐘一致,則合并解決沖突
localVectorClock.merge(requestVectorClock);
localVectorClock.increment(clusterConnection.getClusterId());
vectorClocks.put(clientId, localVectorClock);
ClientSession localClientSession = clientSessionLocalPersistence.get(clientId);
if (!localClientSession.isConnected() && clientSession.isConnected()) {
clientSessionLocalPersistence.persistent(clientId, clientSession, requestTimestamp);
}
}
return null;
});
}