續約的時候,調用的是服務端的InstanceResource#renewLease方法。調用的是InstanceRegistry.renew方法。segmentfault
@PUT public Response renewLease( @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication, @QueryParam("overriddenstatus") String overriddenStatus, @QueryParam("status") String status, @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) { boolean isFromReplicaNode = "true".equals(isReplication); boolean isSuccess = registry.renew(app.getName(), id, isFromReplicaNode); //其餘略 return response; }
InstanceRegistry.renew會發一個EventPublish的續約監聽(代碼略),而後調用PeerAwareInstanceRegistryImpl#renew。PeerAwareInstanceRegistryImpl#renew。會調用AbstractInstanceRegistry#renew,若是成功,會進行節點間的複製。緩存
public boolean renew(final String appName, final String id, final boolean isReplication) { if (super.renew(appName, id, isReplication)) { replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication); return true; } return false; }
AbstractInstanceRegistry#renew,獲取續約,若是有的話。app
public boolean renew(String appName, String id, boolean isReplication) { RENEW.increment(isReplication); Map<String, Lease<InstanceInfo>> gMap = registry.get(appName); Lease<InstanceInfo> leaseToRenew = null; if (gMap != null) { leaseToRenew = gMap.get(id); } if (leaseToRenew == null) { RENEW_NOT_FOUND.increment(isReplication); logger.warn("DS: Registry: lease doesn't exist, registering resource: {} - {}", appName, id); return false; } else { // 其餘略 renewsLastMin.increment(); leaseToRenew.renew(); return true; } }
Lease#renew,更新lastUpdateTimestamp時間。ide
public void renew() { lastUpdateTimestamp = System.currentTimeMillis() + duration; }
綜上,續約就是更改lastUpdateTimestamp的時間。ui
註冊調用的是server的ApplicationResource#addInstance,他調用InstanceRegistry#register,和上面同樣,這個會發布註冊監聽。this
public Response addInstance(InstanceInfo info, @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) { //其餘略 registry.register(info, "true".equals(isReplication)); return Response.status(204).build(); // 204 to be backwards compatible }
InstanceRegistry#register會調用PeerAwareInstanceRegistryImpl#register,實際的註冊是AbstractInstanceRegistry#register方法,這個方法在Eureka - Server服務啓動已經提過了,這裏略。註冊成功後,會進行節點間的複製。spa
@Override public void register(final InstanceInfo info, final boolean isReplication) { int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } super.register(info, leaseDuration, isReplication); replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication); }
下線調用的是InstanceResource#cancelLease,而後他會調用InstanceRegistry#cancel(代碼略)發佈下線監聽。code
@DELETE public Response cancelLease( @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) { //其餘略 boolean isSuccess = registry.cancel(app.getName(), id, "true".equals(isReplication)); //其餘略 }
InstanceRegistry#cancel調用PeerAwareInstanceRegistryImpl#cancel,下線成功後,會進行節點間的複製。server
@Override public boolean cancel(final String appName, final String id, final boolean isReplication) { if (super.cancel(appName, id, isReplication)) { replicateToPeers(Action.Cancel, appName, id, null, null, isReplication); return true; } return false; }
AbstractInstanceRegistry#cancel會調用AbstractInstanceRegistry#internalCancel,這個是核心代碼。這裏會設置取消時間戳,並過時讀寫緩存。blog
@Override public boolean cancel(String appName, String id, boolean isReplication) { return internalCancel(appName, id, isReplication); } protected boolean internalCancel(String appName, String id, boolean isReplication) { read.lock(); try { //其餘略 // 設置取消的時間戳 leaseToCancel.cancel(); //其餘略 // 過時緩存,這裏是讀寫緩存 invalidateCache(appName, vip, svip); } } finally { read.unlock(); } // 用於自我保護 synchronized (lock) { if (this.expectedNumberOfClientsSendingRenews > 0) { // Since the client wants to cancel it, reduce the number of clients to send renews. this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1; updateRenewsPerMinThreshold(); } } return true; }
另外,Eureka - Server服務啓動提到了EvictionTask#run,他會清除過時的實例,他也會調用AbstractInstanceRegistry#internalCancel方法,因此讀寫緩存的圖更改成:
咱們看到,續約的時候lastUpdateTimestamp = System.currentTimeMillis() + duration;
,判斷是否過時的時候,又加了一個duration,因此他剔除的並非90秒沒有心跳的,而是180秒沒有心跳的。
public boolean isExpired(long additionalLeaseMs) { return (evictionTimestamp > 0 || System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs)); }