Spring Cloud 2.2.2 源碼之五十六nacos服務端處理註冊實例請求四
Service模型大致結構
DistroConsistencyServiceImpl的put臨時實例集合一致性服務
添加集合,然後
@Override
public void put(String key, Record value) throws NacosException {
onPut(key, value);
taskDispatcher.addTask(key);
}
onPut添加臨時實例集合
如果是臨時的就添加一個Datum
,將實例集合放入。如果有監聽器監聽的,立即通知,否則就返回,怎麼通知的後面說。
//臨時的實例
public void onPut(String key, Record value) {
//如果是臨時的服務實例集合
if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
Datum<Instances> datum = new Datum<>();//創建臨時數據
datum.value = (Instances) value;
datum.key = key;
datum.timestamp.incrementAndGet();
dataStore.put(key, datum);//放進一個map裏
}
if (!listeners.containsKey(key)) {//沒有監聽器就返回
return;
}
//有監聽立即通知服務有改變
notifier.addTask(key, ApplyAction.CHANGE);
}
RaftConsistencyServiceImpl的put永久實例集合一致性服務
和raft
選舉算法有關。
@Override
public void put(String key, Record value) throws NacosException {
try {
raftCore.signalPublish(key, value);
} catch (Exception e) {
Loggers.RAFT.error("Raft put failed.", e);
throw new NacosException(NacosException.SERVER_ERROR, "Raft put failed, key:" + key + ", value:" + value, e);
}
}
signalPublish
這個其實涉及到raft
選舉的協議,如果本服務不是leader
就要交給leader
去處理,就發一個http
請求給leader
,leader
接受到之後還是會到他的signalPublish
裏處理。如果是leader
的話就進行服務實例改變通知,通知本地的監聽器,並且要同步到其他結點,使用過半機制,剛好CountDownLatch
可以用,只要有過半響應成功就算同步成功,具體的細節後面會講。
public void signalPublish(String key, Record value) throws Exception {
if (!isLeader()) {//不是leader
JSONObject params = new JSONObject();
params.put("key", key);
params.put("value", value);
Map<String, String> parameters = new HashMap<>(1);
parameters.put("key", key);
//交給leader去做/v1/ns/raft/datum
raftProxy.proxyPostLarge(getLeader().ip, API_PUB, params.toJSONString(), parameters);
return;
}
try {//是leader
OPERATE_LOCK.lock();
long start = System.currentTimeMillis();
final Datum datum = new Datum();
datum.key = key;
datum.value = value;
if (getDatum(key) == null) {
datum.timestamp.set(1L);
} else {
datum.timestamp.set(getDatum(key).timestamp.incrementAndGet());
}
JSONObject json = new JSONObject();
json.put("datum", datum);
json.put("source", peers.local());
//發佈數據改變通知
onPublish(datum, peers.local());
final String content = JSON.toJSONString(json);
//只要過半的結點數
final CountDownLatch latch = new CountDownLatch(peers.majorityCount());
for (final String server : peers.allServersIncludeMyself()) {//遍歷所有結點
if (isLeader(server)) {//自己算一次
latch.countDown();
continue;
}
final String url = buildURL(server, API_ON_PUB);///v1/ns/raft/datum/commit
HttpClient.asyncHttpPostLarge(url, Arrays.asList("key=" + key), content, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
Loggers.RAFT.warn("[RAFT] failed to publish data to peer, datumId={}, peer={}, http code={}",
datum.key, server, response.getStatusCode());
return 1;
}
latch.countDown();//異步完成
return 0;
}
@Override
public STATE onContentWriteCompleted() {
return STATE.CONTINUE;
}
});
}
//等待半數完成
if (!latch.await(UtilsAndCommons.RAFT_PUBLISH_TIMEOUT, TimeUnit.MILLISECONDS)) {
// only majority servers return success can we consider this update success
Loggers.RAFT.error("data publish failed, caused failed to notify majority, key={}", key);
throw new IllegalStateException("data publish failed, caused failed to notify majority, key=" + key);
}
long end = System.currentTimeMillis();
Loggers.RAFT.info("signalPublish cost {} ms, key: {}", (end - start), key);
} finally {
OPERATE_LOCK.unlock();
}
}
TaskDispatcher的addTask
負載均衡的獲取一個任務執行器,添加一個新任務,這個就是來做數據同步的,具體做什麼後面一起說。
public void addTask(String key) {
taskSchedulerList.get(UtilsAndCommons.shakeUp(key, cpuCoreCount)).addTask(key);
}
TaskScheduler的addTask
放到一個阻塞隊列裏,做數據同步,同步到其他結點,具體的任務後面說。
private BlockingQueue<String> queue = new LinkedBlockingQueue<>(128 * 1024);
public void addTask(String key) {
queue.offer(key);
}
這樣,服務實例的註冊流程基本說完了,我們可以總結下。
- 如果服務不存在,就創建服務,並開啓心跳檢查,來檢查服務中的每個實例,並將服務註冊到監聽器集合中,因爲服務本身實現了監聽器集合,其他集羣的服務數據有改動的話會通知服務。
- 添加服務實例到服務集羣中,沒有集羣就創建一個,並刷新老的服務實例集合。並將服務
key
添加到同步任務中,同步任務根據數量和超時限制進行服務器集羣的同步。
其實中間可能還開啓了好多任務,比如心跳檢查Service
的ClientBeatCheckTask
,Cluster
的HealthCheckTask
,數據同步的TaskDispatcher
,服務實例改變的通知器Notifier
任務。
好了,今天就到這裏了,希望對學習理解有幫助,大神看見勿噴,僅爲自己的學習理解,能力有限,請多包涵。