1. eureka server端实例存储结构
ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
其中key = appName,value = ConcurrentHashMap
//存储覆盖状态
protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
.newBuilder().initialCapacity(500)
.expireAfterAccess(1, TimeUnit.HOURS)
.<String, InstanceStatus>build().asMap();
1.1 eureka server status
状态 | 描述 |
---|---|
starting | 实例初始化状态,此状态主要给实例预留初始化时间 |
down | 当健康检查失败时,实例的状态转变到down |
up | 正常服务状态 |
out_of_service | 不参与接收服务 。但是服务正常 |
unknown | 未知状态 |
1.2 与server status有关的方法
方法 | 描述 | 调用场景 |
---|---|---|
register | 注册实例 | 1.实例启动的时候开始注册;2.心跳404时开始注册实例 |
cancel | 取消server注册的某一实例 | 客户端shutdown实例后,调用cancel |
renew | 续租请求 | 客户端定时心跳时调用该方法。deleteStatusOvrride方法调用后会触发renew续租失败,触发重新注册实例。 |
statusUpdate | 状态更新方法 | 主要调用的时机时让此service下线,不在接收请求。或者让下线的服务重新上线。 |
deleteStatusOverride | 移除实例的覆盖状态 | 实例的覆盖状态移除后,覆盖状态将变成unknown |
2.假如InstanceInfo中只有status状态
- client端调用updateStatus更新实例状态到out_of_service 。即status = out_of_service。此时client的状态是up状态
- server端此实例的状态现在是out_of_service。并且标记responseCache无效。
- 客户端等待定时器定时更新实例的状态。但是由于时间间隔没有更新,所以client的状态还是up状态。
- client端发送续租renew,由于此时客户端的状态是up状态,server端的状态是out_of_service。因此此前客户端调用的实例下线状态有被改回到原来的状态up.
3.eureka引入overriddenstatus用来解决状态被覆盖问题
- 客户端调用updateStatus方法时,同时更新server端实例的status和overriddenStatus状态。
- 客户端调用renew方法时,也要更新server端实例的status和overriddenstatus状态,但是有一下规则的
(1):如果客户端上传的实例状态是down或者starting,表明客户端是重启或者healthCheck失败。此时这个实例不能作为服务提供服务。因此即使客户端调用updateStatus把实例状态更新为up,也是没用的。此时客户端实例的准确状态就是down或者starting。
(2):如果客户端的实例是up或者out_of_service,此时是不可信的。就像第二大节介绍的那样。有可能client端的实例状态已被改变,此时要使用overriddenstatus状态作为当前实例的状态,避免被覆盖。
(3):(2)中的overriddenstatus有可能不存在,缓存失效,此时要使用server端已经存在的实例的状态。
4. 覆盖状态规则的源码实现
源码实现中使用了:组合模式Composite
/**
* 首先检查 这个实例是starting 还是down的。然后在检查已知明确的override
* 最后我们检查已经存在续租实例潜在的状态
*/
this.instanceStatusOverrideRule = new FirstMatchWinsCompositeRule(new DownOrStartingRule(),
new OverrideExistsRule(overriddenInstanceStatusMap), new LeaseExistsRule());
4.1 组合模式实现FirstMatchWinsCompositeRule
/**
* This rule takes an ordered list of rules and returns the result of the first match or the
* result of the {@link AlwaysMatchInstanceStatusRule}.
*
* 这个规则包含一个有序的规则集合,如果有匹配的则返回第一个匹配的结果
* Created by Nikos Michalakis on 7/13/16.
*/
public class FirstMatchWinsCompositeRule implements InstanceStatusOverrideRule {
/**实例匹配规则集合*/
private final InstanceStatusOverrideRule[] rules;
private final InstanceStatusOverrideRule defaultRule;
private final String compositeRuleName;
public FirstMatchWinsCompositeRule(InstanceStatusOverrideRule... rules) {
this.rules = rules;
this.defaultRule = new AlwaysMatchInstanceStatusRule();
// Let's build up and "cache" the rule name to be used by toString();
List<String> ruleNames = new ArrayList<>(rules.length+1);
for (int i = 0; i < rules.length; ++i) {
ruleNames.add(rules[i].toString());
}
ruleNames.add(defaultRule.toString());
compositeRuleName = ruleNames.toString();
}
/**获取第一个匹配的状态*/
@Override
public StatusOverrideResult apply(InstanceInfo instanceInfo,
Lease<InstanceInfo> existingLease,
boolean isReplication) {
for (int i = 0; i < this.rules.length; ++i) {
StatusOverrideResult result = this.rules[i].apply(instanceInfo, existingLease, isReplication);
if (result.matches()) {
return result;
}
}
return defaultRule.apply(instanceInfo, existingLease, isReplication);
}
@Override
public String toString() {
return this.compositeRuleName;
}
}
4.2 单个规则源码介绍
本文只介绍DownOrStartingRule,其余的不做详细介绍
/**
* This rule matches if the instance is DOWN or STARTING.
*
* 这个规则匹配 实例是 DOWN的还是starting
* Created by Nikos Michalakis on 7/13/16.
*/
public class DownOrStartingRule implements InstanceStatusOverrideRule {
private static final Logger logger = LoggerFactory.getLogger(DownOrStartingRule.class);
@Override
public StatusOverrideResult apply(InstanceInfo instanceInfo,
Lease<InstanceInfo> existingLease,
boolean isReplication) {
// ReplicationInstance is DOWN or STARTING - believe that, but when the instance says UP, question that
// The client instance sends STARTING or DOWN (because of heartbeat failures), then we accept what
// the client says. The same is the case with replica as well.
// The OUT_OF_SERVICE from the client or replica needs to be confirmed as well since the service may be
// currently in SERVICE
/**
* 同步实例是down或者starting的时候是可信的。因为只有在心跳失败时,客户端才会发送down或者starting
*
* 但是当实例的状态是UP 或者OUT_OF_SERVICE时。需要再次确认。
*/
if ((!InstanceInfo.InstanceStatus.UP.equals(instanceInfo.getStatus()))
&& (!InstanceInfo.InstanceStatus.OUT_OF_SERVICE.equals(instanceInfo.getStatus()))) {
logger.debug("Trusting the instance status {} from replica or instance for instance {}",
instanceInfo.getStatus(), instanceInfo.getId());
return StatusOverrideResult.matchingStatus(instanceInfo.getStatus());
}
return StatusOverrideResult.NO_MATCH;
}
@Override
public String toString() {
return DownOrStartingRule.class.getName();
}
}