文章目录
一、服务注册源码
1.1 服务注册入口ApplicationResource#addInstance
在
eureka
中使用的web
框架是jersey
,和springMVC
差不多,这里重点就是调用注册表注册服务
1.1.1 PeerAwareInstanceRegistryImpl#register
这里重点有两个步骤
- 更新本地注册表,把待注册的实例添加到本地注册表中。
- 同步给集群间其他节点。
1.1.1.1 AbstractInstanceRegistry#register
这里代码比较长重要有以下这几个步骤
- 更新注册表信息,这里注册表就是一个
map(key是appName,value是Lease(InstanceInfo)),
这个lease
其实就是租约的相关信息,其中会包含实例信息,ip,port
和最后一次续约的时间戳,租期等,这里如果判断之前在注册表中就存在的话,看有没有变更过,通过变更时间来判断,如果没有,则继续使用原有的instance
信息。如果是第一次注册肯定注册表里没有实例信息,那么就会将expectedNumberOfClientsSendingRenew+1
,这个表示需要续约的客户端数量,然后就是调用updateRenewsPerMinThreshold
更新下一接受续约的最少的阈值(跟自我保护有关,后续会讲),然后生成租约信息放入注册表中。- 塞入最近改变队列中,更新最后更新时间,这里最近改变队列
- 失效本地缓存(服务发现的时候会先去本地的一级二级缓存中获取,没有的话才会才回去注册表中查找)
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
// 获取读锁
read.lock();
try {
// 先从注册表中 获取对应 appName 的 map 集合
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);
// 不存在就创建,并且塞入 appName 的 map 集合
if (gMap == null) {
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap =
new ConcurrentHashMap<String, Lease<InstanceInfo>>();
// 存在就不会 put ,并将存在的那个返回来,不能存在的话进行 put ,返回 null
// 试想该地使用 putIfAbsent 的作用 : double check, 虽然使用了concurrentHashMap
// 保证了元素的增删改查没有问题,但是还是不能保证 gNewMap 只存在一个
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) {
gMap = gNewMap;
}
}
// 获取该实例 id 对应的租约信息
Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
// Retain the last dirty timestamp without overwriting it, if there is already a lease
// 如果是存在
if (existingLease != null && (existingLease.getHolder() != null)) {
Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
// this is a > instead of a >= because if the timestamps are equal, we still take the remote transmitted
// InstanceInfo instead of the server local copy.
if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" +
" than the one that is being registered {}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant");
registrant = existingLease.getHolder();
}
// 如不存在的话
} else {
// The lease does not exist and hence it is a new registration
synchronized (lock) {
// 这个是与自我保护机制有关的
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to register it, increase the number of clients sending renews
// 每有一个新的客户端注册进来,就会 +1 ,标识回来要发送心跳的客户端 +1
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
// 更新下自己能容忍的最少心跳数量
updateRenewsPerMinThreshold();
}
}
logger.debug("No previous lease information found; it is new registration");
}
Lease<InstanceInfo> lease = new Lease<>(registrant, leaseDuration);
if (existingLease != null) {
// 服务启动时间ServiceUp
lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
}
// 塞到注册表中
gMap.put(registrant.getId(), lease);
// 放到registed 队列中
recentRegisteredQueue.add(new Pair<Long, String>(
System.currentTimeMillis(),
registrant.getAppName() + "(" + registrant.getId() + ")"));
// This is where the initial state transfer of overridden status happens
if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {
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());
}
}
// 从本地缓存中获取该实例状态
InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());
if (overriddenStatusFromMap != null) {
// 存在的话设置进去
logger.info("Storing overridden status {} from map", overriddenStatusFromMap);
registrant.setOverriddenStatus(overriddenStatusFromMap);
}
// Set the status based on the overridden status rules
InstanceStatus overriddenInstanceStatus =
getOverriddenInstanceStatus(registrant, existingLease, isReplication);
registrant.setStatusWithoutDirty(overriddenInstanceStatus);
// If the lease is registered with UP status, set lease service up timestamp
// 实例状态是 up 的话,设置下服务的启动时间
if (InstanceStatus.UP.equals(registrant.getStatus())) {
lease.serviceUp();
}
registrant.setActionType(ActionType.ADDED);
// 塞入最近改变队列中
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
// 更新最后更新时间
registrant.setLastUpdatedTimestamp();
// 本地缓存失效
invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
logger.info("Registered instance {}/{} with status {} (replication={})",
registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);
} finally {
read.unlock();
}
}
1.1.1.2 AbstractInstanceRegistry#getDeltaRetentionTask
上面我们把租约信息放到最近改变队列中,然后我们看在什么时候从队列中获取值的,在这个方法中会去遍历队列中的值,这里其实就是在队列中保留三分钟,三分钟过后就从队列中删掉,会有一个定时任务定时调用这个方法,这个后续增量获取的时候会说,大家现在只需要知道这个队列中会保存三分钟内改变信息。
protected AbstractInstanceRegistry(EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, ServerCodecs serverCodecs) {
this.serverConfig = serverConfig;
this.clientConfig = clientConfig;
this.serverCodecs = serverCodecs;
this.recentCanceledQueue = new CircularQueue<Pair<Long, String>>(1000);
this.recentRegisteredQueue = new CircularQueue<Pair<Long, String>>(1000);
this.renewsLastMin = new MeasuredRate(1000 * 60 * 1);
this.deltaRetentionTimer.schedule(getDeltaRetentionTask(),
serverConfig.getDeltaRetentionTimerIntervalInMs(),
serverConfig.getDeltaRetentionTimerIntervalInMs());
}
private TimerTask getDeltaRetentionTask() {
return new TimerTask() {
@Override
public void run() {
Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator();
while (it.hasNext()) {
if (it.next().getLastUpdateTime() <
System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {
it.remove();
} else {
break;
}
}
}
};
}