一信轿、前言
這篇文章嘗試通過 eureka 心跳包的整個復(fù)制流程來帶大家去理解eureka的復(fù)制算法。
首先大家需要明白奢啥,eureka跟zookeeper不一樣髓抑,eureka是選擇了A(可用性)P(分區(qū)容忍)的實現(xiàn),而zookeeper選擇了C(一致性)P(分區(qū)容忍)的實現(xiàn)仗处。這也就造就了eureka和zookeeper在實現(xiàn)上不一樣眯勾,由于不需要保證一致性枣宫,eureka不需要有一個中心結(jié)點的存在,所有的結(jié)點都是對等的吃环。
對等結(jié)點
二镶柱、分析
1.eureka-client上報心跳包到eureka-server
請求報文如下:
Hypertext Transfer Protocol
PUT /eureka/v2/apps/SAMPLEREGISTERINGSERVICE/201709-07262?status=UP&lastDirtyTimestamp=1552742035025 HTTP/1.1\r\n
DiscoveryIdentity-Name: DefaultClient\r\n
DiscoveryIdentity-Version: 1.4\r\n
DiscoveryIdentity-Id: 172.19.10.230\r\n
Accept-Encoding: gzip\r\n
Content-Length: 0\r\n
Host: localhost:8080\r\n
Connection: Keep-Alive\r\n
User-Agent: Java-EurekaClient/v<version_unknown>\r\n
\r\n
[Full request URI: http://localhost:8080/eureka/v2/apps/SAMPLEREGISTERINGSERVICE/201709-07262?status=UP&lastDirtyTimestamp=1552742035025]
[HTTP request 1/1]
[Response in frame: 7296]
客戶端把應(yīng)用存活的信息上報到eureka-server。
具體可以看上一篇分析:eureka源碼分析-DiscoveryClient
服務(wù)端的代碼入口:InstanceResource.renewLease
/**
* A put request for renewing lease from a client instance.
*
* @param isReplication
* a header parameter containing information whether this is
* replicated from other nodes.
* @param overriddenStatus
* overridden status if any.
* @param status
* the {@link InstanceStatus} of the instance.
* @param lastDirtyTimestamp
* last timestamp when this instance information was updated.
* @return response indicating whether the operation was a success or
* failure.
*/
@PUT
public Response renewLease(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("overriddenstatus") String overriddenStatus,
@QueryParam("status") String status,
@QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
通過這個方法模叙,eureka會把客戶端上報的信息同步到內(nèi)存,當(dāng)然這里還會涉及到實例狀態(tài)沖突的問題鞋屈,這里暫時不作討論范咨。
2. 增量復(fù)制
增量復(fù)制
eureka-server把結(jié)點信息復(fù)制到其他eureka-server結(jié)點
通過方法的調(diào)用鏈我們跟蹤到PeerAwareInstanceRegistryImpl.renew這個方法。
調(diào)用棧
/*
* (non-Javadoc)
*
* @see com.netflix.eureka.registry.InstanceRegistry#renew(java.lang.String,
* java.lang.String, long, boolean)
*/
public boolean renew(final String appName, final String id, final boolean isReplication) {
if (super.renew(appName, id, isReplication)) {
//復(fù)制信息到其他結(jié)點
replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);
return true;
}
return false;
}
/**
* Replicates all eureka actions to peer eureka nodes except for replication
* traffic to this node.
*
*/
private void replicateToPeers(Action action, String appName, String id,
InstanceInfo info /* optional */,
InstanceStatus newStatus /* optional */, boolean isReplication) {
Stopwatch tracer = action.getTimer().start();
try {
if (isReplication) {
numberOfReplicationsLastMin.increment();
}
// If it is a replication already, do not replicate again as this will create a poison replication
if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
return;
}
for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
// If the url represents this host, do not replicate to yourself.
if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
continue;
}
replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
}
} finally {
tracer.stop();
}
}
- 最初收到心跳包的eureka-server的isReplication為false厂庇,因此會把節(jié)點信息往其他的eureka-server結(jié)點傳遞渠啊。
- 當(dāng)傳遞到下一個結(jié)點的時候isReplication已經(jīng)為true,表示該結(jié)點信息由其他eureka-server結(jié)點復(fù)制過來权旷,這時候下一個結(jié)點就不會繼續(xù)往下傳遞替蛉。這主要是為了避免造成死循環(huán)。
- 在數(shù)據(jù)復(fù)制過程中失敗(結(jié)點重啟拄氯?網(wǎng)絡(luò)分區(qū))躲查,這種場景如何解決?除了增量復(fù)制以外译柏,eureka-server還會定時做結(jié)點間的全量復(fù)制來保證數(shù)據(jù)的一致性(30s一次)镣煮。
3. 全量復(fù)制
具體源碼可以參考之前的分析
eureka源碼分析-復(fù)制算法(一)
image.png
三、總結(jié)
- eureka-server的復(fù)制算法是依賴增量復(fù)制+全量復(fù)制實現(xiàn)的鄙麦。區(qū)別于zookeeper典唇,這里沒有l(wèi)eader的概念,所有的結(jié)點都是平等的胯府,因此數(shù)據(jù)并不保證一致性介衔。
- eureka-server數(shù)據(jù)的狀態(tài)沖突如何解決?后面考慮單獨抽一篇文章來分析