應(yīng)用場景
在實(shí)際開發(fā)使用過程當(dāng)中消略,在Eureka Admin控制臺(tái)上,我們想強(qiáng)制下線某個(gè)服務(wù) 瞎抛,就需要用到覆蓋狀態(tài)的
概念艺演,其實(shí)說白了,就是在給實(shí)例存儲(chǔ)另外一個(gè)狀態(tài)桐臊,當(dāng)續(xù)約胎撤,注冊的時(shí)候,以這個(gè)覆蓋狀態(tài)為準(zhǔn) 断凶。
覆蓋狀態(tài)
設(shè)置覆蓋狀態(tài)
程序入口: com.netflix.eureka.resources.InstanceResource
@PUT
@Path("status")
public Response statusUpdate(
@QueryParam("value") String newStatus,
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
try {
// 判斷實(shí)例是否存在伤提,不存在則更新失敗
if (registry.getInstanceByAppAndId(app.getName(), id) == null) {
logger.warn("Instance not found: {}/{}", app.getName(), id);
return Response.status(Status.NOT_FOUND).build();
}
// 更新狀態(tài)
boolean isSuccess = registry.statusUpdate(app.getName(), id,
InstanceStatus.valueOf(newStatus), lastDirtyTimestamp,
"true".equals(isReplication));
if (isSuccess) {
logger.info("Status updated: " + app.getName() + " - " + id
+ " - " + newStatus);
return Response.ok().build();
} else {
logger.warn("Unable to update status: " + app.getName() + " - "
+ id + " - " + newStatus);
return Response.serverError().build();
}
} catch (Throwable e) {
logger.error("Error updating instance {} for status {}", id,
newStatus);
return Response.serverError().build();
}
}
1.判斷實(shí)例在服務(wù)端是否存在,不存在則返回更新失敗
2.調(diào)用更新狀態(tài)的接口
public boolean statusUpdate(final String appName, final String id,
final InstanceStatus newStatus, String lastDirtyTimestamp,
final boolean isReplication) {
// 調(diào)用父類的狀態(tài)更新
if (super.statusUpdate(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {
// Eureka Server 集群同步
replicateToPeers(Action.StatusUpdate, appName, id, null, newStatus, isReplication);
return true;
}
return false;
}
public boolean statusUpdate(String appName, String id,
InstanceStatus newStatus, String lastDirtyTimestamp,
boolean isReplication) {
try {
//使用讀鎖
read.lock();
// 更新狀態(tài)的次數(shù) 狀態(tài)統(tǒng)計(jì)
STATUS_UPDATE.increment(isReplication);
// 從本地?cái)?shù)據(jù)里面獲取實(shí)例信息认烁,
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> lease = null;
if (gMap != null) {
lease = gMap.get(id);
}
// 實(shí)例不存在肿男,則直接返回床绪,更細(xì)你失敗
if (lease == null) {
return false;
} else {
// 執(zhí)行一下lease的renew方法袱蚓,里面主要是更新了這個(gè)instance的最后更新時(shí)間辕漂。
lease.renew();
// 獲取instance實(shí)例信息
InstanceInfo info = lease.getHolder();
// Lease is always created with its instance info object.
// This log statement is provided as a safeguard, in case this invariant is violated.
if (info == null) {
logger.error("Found Lease without a holder for instance id {}", id);
}
// 當(dāng)instance信息不為空時(shí)
if ((info != null) && !(info.getStatus().equals(newStatus))) {
// Mark service as UP if needed
// 如果新狀態(tài)是UP的狀態(tài)介袜,那么啟動(dòng)一下serviceUp() , 主要是更新服務(wù)的注冊時(shí)間摔蓝。
if (InstanceStatus.UP.equals(newStatus)) {
lease.serviceUp();
}
// 將instance Id 和這個(gè)狀態(tài)的映射信息放入覆蓋緩存MAP里面去
overriddenInstanceStatusMap.put(id, newStatus);
// Set it for transfer of overridden status to replica on
// replica start up
// 設(shè)置覆蓋狀態(tài)到實(shí)例信息里面去
info.setOverriddenStatus(newStatus);
long replicaDirtyTimestamp = 0;
if (lastDirtyTimestamp != null) {
replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp);
}
//
// 如果replicaDirtyTimestamp 的時(shí)間大于instance的getLastDirtyTimestamp() ,則更新一下
// 此處主要作用是為了設(shè)置instance的status 為新狀態(tài)
if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) {
info.setLastDirtyTimestamp(replicaDirtyTimestamp);
info.setStatusWithoutDirty(newStatus);
} else {
info.setStatus(newStatus);
}
// 將instance的變化放入變化隊(duì)列里面去灿椅,客戶端增量獲取注冊信息時(shí)茂附,會(huì)用到舒岸。
info.setActionType(ActionType.MODIFIED);
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
info.setLastUpdatedTimestamp();
// 緩存過期
invalidateCache(appName, info.getVIPAddress(), info.getSecureVipAddress());
}
return true;
}
} finally {
read.unlock();
}
}
步驟如下:
1.獲取實(shí)例信息撼港,判斷instance信息是否為空
2.將instanceId和覆蓋狀態(tài)坪它,放入overriddenInstanceStatusMap 這個(gè)緩存MAP中進(jìn)行了存儲(chǔ) , 過期時(shí)間默認(rèn)為 1小時(shí)
3.更新instance的狀態(tài)
4.添加instance Change 的記錄進(jìn)入隊(duì)列
5.清除緩存餐胀。
刪除覆蓋狀態(tài)
程序入口: com.netflix.eureka.resources.InstanceResource
@DELETE
@Path("status")
public Response deleteStatusUpdate(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("value") String newStatusValue,
@QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
try {
// 判斷應(yīng)用是否為空
if (registry.getInstanceByAppAndId(app.getName(), id) == null) {
logger.warn("Instance not found: {}/{}", app.getName(), id);
return Response.status(Status.NOT_FOUND).build();
}
// 刪除覆蓋狀態(tài)
InstanceStatus newStatus = newStatusValue == null ? InstanceStatus.UNKNOWN : InstanceStatus.valueOf(newStatusValue);
boolean isSuccess = registry.deleteStatusOverride(app.getName(), id,
newStatus, lastDirtyTimestamp, "true".equals(isReplication));
if (isSuccess) {
logger.info("Status override removed: " + app.getName() + " - " + id);
return Response.ok().build();
} else {
logger.warn("Unable to remove status override: " + app.getName() + " - " + id);
return Response.serverError().build();
}
} catch (Throwable e) {
logger.error("Error removing instance's {} status override", id);
return Response.serverError().build();
}
}
步驟說明:
1.判斷應(yīng)用信息是否為空
2.執(zhí)行刪除覆蓋狀態(tài)的接口
@Override
public boolean deleteStatusOverride(String appName, String id,
InstanceStatus newStatus,
String lastDirtyTimestamp,
boolean isReplication) {
// 刪除覆蓋狀態(tài)
if (super.deleteStatusOverride(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {
// Eureka Server 集群同步
replicateToPeers(Action.DeleteStatusOverride, appName, id, null, null, isReplication);
return true;
}
return false;
}
@Override
public boolean deleteStatusOverride(String appName, String id,
InstanceStatus newStatus,
String lastDirtyTimestamp,
boolean isReplication) {
try {
// 上讀鎖
read.lock();
// 覆蓋狀態(tài)刪除次數(shù)統(tǒng)計(jì)
STATUS_OVERRIDE_DELETE.increment(isReplication);
// 從本地獲取appName對應(yīng)的實(shí)例信息
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> lease = null;
if (gMap != null) {
lease = gMap.get(id);
}
if (lease == null) {
return false;
} else {
//
lease.renew();
InstanceInfo info = lease.getHolder();
// Lease is always created with its instance info object.
// This log statement is provided as a safeguard, in case this invariant is violated.
if (info == null) {
logger.error("Found Lease without a holder for instance id {}", id);
}
// 將這個(gè)instanceID 對應(yīng)的覆蓋狀態(tài)信息從MAP中移除
InstanceStatus currentOverride = overriddenInstanceStatusMap.remove(id);
if (currentOverride != null && info != null) {
// 設(shè)置instanceInfo的覆蓋狀態(tài)為UNKONW哟楷, 這是初始狀態(tài)
info.setOverriddenStatus(InstanceStatus.UNKNOWN);
// 設(shè)置instance的status為最新的狀態(tài)
info.setStatus(newStatus);
long replicaDirtyTimestamp = 0;
if (lastDirtyTimestamp != null) {
replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp);
}
// If the replication's dirty timestamp is more than the existing one, just update
// it to the replica's.
if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) {
info.setLastDirtyTimestamp(replicaDirtyTimestamp);
}
// 將instance的變化放入變化隊(duì)列里面去,客戶端增量獲取注冊信息時(shí)否灾,會(huì)用到卖擅。
info.setActionType(ActionType.MODIFIED);
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
// 更新instance的最后更新時(shí)間
info.setLastUpdatedTimestamp();
// 緩存過期
invalidateCache(appName, info.getVIPAddress(), info.getSecureVipAddress());
}
return true;
}
} finally {
read.unlock();
}
}
步驟說明:
1.從本地的CurrentHashMap中獲取appName對應(yīng)的應(yīng)用信息,然后通過instanceId獲取該應(yīng)用對應(yīng)的機(jī)器
2.移除overriddenInstanceStatusMap中該instanceId對應(yīng)的覆蓋狀態(tài)
3.設(shè)置instanceInfo的覆蓋狀態(tài)為UNKNOW墨技,這個(gè)是他的初始狀態(tài)惩阶。
- 設(shè)置instance的status為新狀態(tài)
5.添加instance Change 的記錄進(jìn)入隊(duì)列
6.清除緩存。
實(shí)際應(yīng)用
客戶端注冊
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
// .....省略N多代碼
// 判斷instance的的覆蓋狀態(tài)是否等于UNKONW (默認(rèn)狀態(tài)下就是等于UNKONW)
if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {
// 如果不等于扣汪,則說明被修改過断楷,放入overriddenInstanceStatusMap
logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the "
+ "overrides", registrant.getOverriddenStatus(), registrant.getId());
if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {
logger.info("Not found overridden id {} and hence adding it", registrant.getId());
overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());
}
}
// overriddenInstanceStatusMap 里面是否存在這個(gè)instanceId的覆蓋狀態(tài)
InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());
// 如果存在,則設(shè)置進(jìn)去
if (overriddenStatusFromMap != null) {
logger.info("Storing overridden status {} from map", overriddenStatusFromMap);
registrant.setOverriddenStatus(overriddenStatusFromMap);
}
// 獲取instance的狀態(tài)崭别,并設(shè)置進(jìn)去
InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);
registrant.setStatusWithoutDirty(overriddenInstanceStatus);
// .....省略N多代碼
} finally {
read.unlock();
}
}
續(xù)約
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 {
InstanceInfo instanceInfo = leaseToRenew.getHolder();
if (instanceInfo != null) {
// 獲取應(yīng)用實(shí)例的最終狀態(tài)
InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(
instanceInfo, leaseToRenew, isReplication);
// 如果應(yīng)用實(shí)例的最終狀態(tài)為UNKONW冬筒,則無法續(xù)約恐锣。
if (overriddenInstanceStatus == InstanceStatus.UNKNOWN) {
logger.info("Instance status UNKNOWN possibly due to deleted override for instance {}"
+ "; re-register required", instanceInfo.getId());
RENEW_NOT_FOUND.increment(isReplication);
return false;
}
// 應(yīng)用實(shí)例的狀態(tài)和最終的狀態(tài)不一致,則以最終狀態(tài)為準(zhǔn)
if (!instanceInfo.getStatus().equals(overriddenInstanceStatus)) {
Object[] args = {
instanceInfo.getStatus().name(),
instanceInfo.getOverriddenStatus().name(),
instanceInfo.getId()
};
instanceInfo.setStatus(overriddenInstanceStatus);
}
}
renewsLastMin.increment();
leaseToRenew.renew();
return true;
}
}
步驟說明:
1.獲取應(yīng)用實(shí)例的最終狀態(tài)
2.如果最終狀態(tài)為UNKONW舞痰,則無法續(xù)約土榴,返回false ,存在UNKONW的可能性响牛,在deleteStatusOverride()的時(shí)候
存在UNKONW的可能性
3.應(yīng)用實(shí)例的狀態(tài)和最終狀態(tài)不一致玷禽,以最終狀態(tài)為準(zhǔn)。
下線
protected boolean internalCancel(String appName, String id, boolean isReplication) {
try {
// ..... 省略N多代碼
// 將覆蓋狀態(tài)移除
InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
// ..... 省略N多代碼
} finally {
read.unlock();
}
}
步驟說明:
1.移除覆蓋狀態(tài)
過期
和下線一致