When a Eureka client initiates registration via a POST request, it invokes the ApplicationResource.addInstance method. This method performs validation and delegates the registration logic.
@POST
@Consumes({"application/json", "application/xml"})
public Response addInstance(InstanceInfo instanceData,
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String replicationFlag) {
DataCenterInfo dataCenterInfo = instanceData.getDataCenterInfo();
if (dataCenterInfo instanceof UniqueIdentifier) {
String dataCenterId = ((UniqueIdentifier) dataCenterInfo).getId();
if (isBlank(dataCenterId)) {
boolean validationEnabled = "true".equalsIgnoreCase(
serverConfig.getExperimental("registration.validation.dataCenterInfoId"));
if (validationEnabled) {
String errorMessage = "DataCenterInfo of type " + dataCenterInfo.getClass() +
" must contain a valid id";
return Response.status(400).entity(errorMessage).build();
} else if (dataCenterInfo instanceof AmazonInfo) {
AmazonInfo awsInfo = (AmazonInfo) dataCenterInfo;
String instanceIdentifier = awsInfo.get(AmazonInfo.MetaDataKey.instanceId);
if (instanceIdentifier == null) {
awsInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(),
instanceData.getId());
}
} else {
logger.warn("Registering DataCenterInfo of type {} without an appropriate id",
dataCenterInfo.getClass());
}
}
}
registry.register(instanceData, "true".equals(replicationFlag));
return Response.status(204).build();
}
PeerAwareInstanceRegistryImpl.register Method
The registration request is forwarded to PeerAwareInstanceRegistryImpl.register. This method determines the lease duration and replicates the registration to other nodes in the Eureka cluster.
@Override
public void register(final InstanceInfo instanceData, final boolean isReplication) {
int leaseTimeout = Lease.DEFAULT_DURATION_IN_SECS;
if (instanceData.getLeaseInfo() != null && instanceData.getLeaseInfo().getDurationInSecs() > 0) {
leaseTimeout = instanceData.getLeaseInfo().getDurationInSecs();
}
super.register(instanceData, leaseTimeout, isReplication);
replicateToPeers(Action.Register, instanceData.getAppName(), instanceData.getId(),
instanceData, null, isReplication);
}
AbstractInstanceRegistry.register Method
The core registration logic resides in AbstractInstanceRegistry.register. It stores the instance information in a nested ConcurrentHashMap structure.
public void register(InstanceInfo instanceData, int leaseDuration, boolean isReplication) {
try {
read.lock();
Map<string lease="">> appInstances = registry.get(instanceData.getAppName());
REGISTER.increment(isReplication);
if (appInstances == null) {
ConcurrentHashMap<string lease="">> newAppMap = new ConcurrentHashMap<>();
appInstances = registry.putIfAbsent(instanceData.getAppName(), newAppMap);
if (appInstances == null) {
appInstances = newAppMap;
}
}
Lease<instanceinfo> existingLease = appInstances.get(instanceData.getId());
if (existingLease != null && (existingLease.getHolder() != null)) {
Long existingTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
Long newTimestamp = instanceData.getLastDirtyTimestamp();
if (existingTimestamp > newTimestamp) {
instanceData = existingLease.getHolder();
}
} else {
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
updateRenewsPerMinThreshold();
}
}
}
Lease<instanceinfo> newLease = new Lease<>(instanceData, leaseDuration);
if (existingLease != null) {
newLease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
}
appInstances.put(instanceData.getId(), newLease);
recentRegisteredQueue.add(new Pair<>(
System.currentTimeMillis(),
instanceData.getAppName() + "(" + instanceData.getId() + ")"));
if (!InstanceStatus.UNKNOWN.equals(instanceData.getOverriddenStatus())) {
if (!overriddenInstanceStatusMap.containsKey(instanceData.getId())) {
overriddenInstanceStatusMap.put(instanceData.getId(), instanceData.getOverriddenStatus());
}
}
InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(instanceData.getId());
if (overriddenStatusFromMap != null) {
instanceData.setOverriddenStatus(overriddenStatusFromMap);
}
InstanceStatus determinedStatus = getOverriddenInstanceStatus(instanceData, existingLease, isReplication);
instanceData.setStatusWithoutDirty(determinedStatus);
if (InstanceStatus.UP.equals(instanceData.getStatus())) {
newLease.serviceUp();
}
instanceData.setActionType(ActionType.ADDED);
recentlyChangedQueue.add(new RecentlyChangedItem(newLease));
instanceData.setLastUpdatedTimestamp();
invalidateCache(instanceData.getAppName(), instanceData.getVIPAddress(),
instanceData.getSecureVipAddress());
} finally {
read.unlock();
}
}</instanceinfo></instanceinfo></string></string>
The server stores instance data in a two-level map: the outer key is the applicasion name, and the enner key is the instance ID. Each instance is wrapped in a Lease object that manages its lifecycle, including a default 90-second expiration. The registration process also handles lease renewal timestamps, status overrides, and cache invalidation to ensure consistent state across the server.