A Java technology warehouse full of dry stuff:
- Github.com/Wasabi1234/…
- What happens when you add the Start Eureka service registry annotation to the start class?
- Activation and eureka server configuration EurekaServerAutoConfiguration annotations
- EurekaServerMarkerConfiguration
Click here and we find the spring.factories file
- Note the following note
ConditionalOnBean – Conditional injection
Also is when a EurekaServerMarkerConfiguration. Marker. The class, will be injected
@ EnableEurekaServer is a switch, as long as write the annotations, spring will help you get EurekaServerAutoConfiguration class injection.
So why is it okay to inject him?
EurekaServerAutoConfiguration#jerseyFilterRegistration
ApplicationResource#addInstance
It’s like a controller in MVC
/**
* Registers information about a particular instance for an
* {@link com.netflix.discovery.shared.Application}.
*
* @param info
* {@link InstanceInfo} information of the instance.
* @param isReplication
* a header parameter containing information whether this is
* replicated from other nodes.
*/
@POST
@Consumes({"application/json"."application/xml"})
public Response addInstance(InstanceInfo info, @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
// validate that the instanceinfo contains all the necessary required fields
if (isBlank(info.getId())) {
return Response.status(400).entity("Missing instanceId").build();
} else if (isBlank(info.getHostName())) {
return Response.status(400).entity("Missing hostname").build();
} else if (isBlank(info.getIPAddr())) {
return Response.status(400).entity("Missing ip address").build();
} else if (isBlank(info.getAppName())) {
return Response.status(400).entity("Missing appName").build();
} else if(! appName.equals(info.getAppName())) {return Response.status(400).entity("Mismatched appName, expecting " + appName + " but was " + info.getAppName()).build();
} else if (info.getDataCenterInfo() == null) {
return Response.status(400).entity("Missing dataCenterInfo").build();
} else if (info.getDataCenterInfo().getName() == null) {
return Response.status(400).entity("Missing dataCenterInfo Name").build();
}
// handle cases where clients may be registering with bad DataCenterInfo with missing data
DataCenterInfo dataCenterInfo = info.getDataCenterInfo();
if (dataCenterInfo instanceof UniqueIdentifier) {
String dataCenterInfoId = ((UniqueIdentifier) dataCenterInfo).getId();
if (isBlank(dataCenterInfoId)) {
boolean experimental = "true".equalsIgnoreCase(serverConfig.getExperimental("registration.validation.dataCenterInfoId"));
if (experimental) {
String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";
return Response.status(400).entity(entity).build();
} else if (dataCenterInfo instanceof AmazonInfo) {
AmazonInfo amazonInfo = (AmazonInfo) dataCenterInfo;
String effectiveId = amazonInfo.get(AmazonInfo.MetaDataKey.instanceId);
if (effectiveId == null) { amazonInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(), info.getId()); }}else {
logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());
}
}
}
registry.register(info, "true".equals(isReplication));
return Response.status(204).build(); // 204 to be backwards compatible
}
Copy the code
Note the following inheritance diagram
InstanceRegistry#register
PeerAwareInstanceRegistryImpl#register
This is the chain of responsibility mode!!
AbstractInstanceRegistry#register
- Registry K: microservice cluster V: each node in the microservice cluster
What to do when registration information conflicts occur?
According to the last active time, determine which to cover
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
// Concurrent read lock
read.lock();
// First get the microservice name, then get the instance
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);
// It didn't exist at the time of the first registration, so new one
if (gMap == null) {
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
// If you don't have it, put it. Because Registry can still be written! After all, he's not reading within lock range!
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) { gMap = gNewMap; }}// An existing registered node
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)) {
// Get the registration time of the existing node
Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
// The registration time of the node currently being registered
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.
// New nodes are registered when registering ing!
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");
// Then replace the current registered node with the existing one, for the sake of ensuring availability as much as possible, since the old one is still activeregistrant = existingLease.getHolder(); }}else {
// The lease does not exist and hence it is a new registration
synchronized (lock) {
if (this.expectedNumberOfRenewsPerMin > 0) {
// Since the client wants to cancel it, reduce the threshold
/ / (1
// for 30 seconds, 2 for a minute)
this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;
this.numberOfRenewsPerMinThreshold =
(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
}
}
logger.debug("No previous lease information found; it is new registration");
}
// New heartbeat renewal object, including registration information, last operation time, registration event, expiration time, culling time
Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);
if(existingLease ! =null) {
lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
}
// Place the new object in the registry
gMap.put(registrant.getId(), lease);
synchronized (recentRegisteredQueue) {
// Join the registration queue
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
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(); }}Copy the code
The service contract
Notify the Eureka Server Service Provider that the Service is still alive to avoid being removed.
And register implementation idea is basically the same
- Update its Status
- Then synchronize the data to other peers
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) {
// Get the registered node
leaseToRenew = gMap.get(id);
}
if (leaseToRenew == null) {
// If no, re-register for renewal
RENEW_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: lease doesn't exist, registering resource: {} - {}", appName, id);
return false;
} else {
/ / there
// Get the node information first
InstanceInfo instanceInfo = leaseToRenew.getHolder();
if(instanceInfo ! =null) {
// touchASGCache(instanceInfo.getASGName());
InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(
instanceInfo, leaseToRenew, isReplication);
// Check whether the system is down
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;
}
if(! instanceInfo.getStatus().equals(overriddenInstanceStatus)) { logger.info("The instance status {} is different from overridden instance status {} for instance {}. "
+ "Hence setting the status to overridden status", instanceInfo.getStatus().name(),
instanceInfo.getOverriddenStatus().name(),
instanceInfo.getId());
instanceInfo.setStatusWithoutDirty(overriddenInstanceStatus);
}
}
renewsLastMin.increment();
// If normal, renew the contract
leaseToRenew.renew();
return true; }}Copy the code
public enum InstanceStatus {
UP, // Ready to receive traffic
DOWN, // Do not send traffic- healthcheck callback failed
STARTING, // Just about starting- initializations to be done - do not
// send traffic
OUT_OF_SERVICE, // Intentionally shutdown for traffic
UNKNOWN;
public static InstanceStatus toEnum(String s) {
if(s ! =null) {
try {
return InstanceStatus.valueOf(s.toUpperCase());
} catch (IllegalArgumentException e) {
// ignore and fall through to unknown
logger.debug("illegal argument supplied to InstanceStatus.valueOf: {}, defaulting to {}", s, UNKNOWN); }}returnUNKNOWN; }}Copy the code
Renewal takes advantage of the duration of the update, if it is associated with the specified T during registration, otherwise the default duration is DEFAULT_DURATION_IN_SECS, i.e. 90s
Service failure elimination
AbstractInstanceRegistry#postInit
- Eliminating scheduled tasks
public void evict(long additionalLeaseMs) {
logger.debug("Running the evict task");
// Determine whether to enable self-protection
if(! isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled.");
return;
}
// We collect first all expired items, to evict them in random order. For large eviction sets,
// if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,
// the impact should be evenly distributed across all applications.
List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>();
for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {
Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();
if(leaseMap ! =null) {
for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {
Lease<InstanceInfo> lease = leaseEntry.getValue();
if(lease.isExpired(additionalLeaseMs) && lease.getHolder() ! =null) {
// Set saves all culled nodesexpiredLeases.add(lease); }}}}// To compensate for GC pauses or drifting local time, we need to use current registry size as a base for
// triggering self-preservation. Without that we would wipe out full registry.
// The size of the registry is the number of all registered nodes
int registrySize = (int) getLocalRegistrySize();
// Threshold, 0.85
int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());
// Get the deletion threshold
int evictionLimit = registrySize - registrySizeThreshold;
// Take the minimum values of the number of nodes to be removed and the deletion threshold as the number of nodes to be removed under the self-protection mechanism. If you delete more, you still have 85 opens. if you delete less, you lower it.
int toEvict = Math.min(expiredLeases.size(), evictionLimit);
if (toEvict > 0) {
logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit);
// Randomly remove so many
Random random = new Random(System.currentTimeMillis());
for (int i = 0; i < toEvict; i++) {
// Pick a random item (Knuth shuffle algorithm)
int next = i + random.nextInt(expiredLeases.size() - i);
Collections.swap(expiredLeases, i, next);
Lease<InstanceInfo> lease = expiredLeases.get(i);
String appName = lease.getHolder().getAppName();
String id = lease.getHolder().getId();
EXPIRED.increment();
logger.warn("DS: Registry: expired lease for {}/{}", appName, id);
internalCancel(appName, id, false); }}}Copy the code
- Whether the current time is longer than the last operation time + duration + reserved time for synchronization between service clusters
Self-protection mechanism
If a large number of eureka micro-services are deleted in a short period of time, the self-protection mechanism will be turned on to avoid crazy deletion during downtime
Check if the Eureka server is self-protected. When enabled, the server tracks the number of updates it should receive from the server. Any time renewal number below, in accordance with the provisions of the threshold percentage getRenewalPercentThreshold (), the server closes to expire, to avoid the danger will help server maintenance between client and server to registry of the problems of network information. These changes are effective at run time for 15min 85% downtime when the mechanism is turned on
Synchronize the data to other peers
Passing in various actions
- InstanceInfo has the following statuses
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl
PeerAwareInstanceRegistryImpl#register
- InstanceInfo is written to the local Registry and then synchronized to other peer nodes
private void replicateToPeers(Action action, String appName, String id,
InstanceInfo info / * optional * /,
InstanceStatus newStatus / * optional * /.boolean isReplication) {
Stopwatch tracer = action.getTimer().start();
try {
if (isReplication) {
numberOfReplicationsLastMin.increment();
}
// If it is already a copy, do not copy again, as this will create a toxic copy!
// If the instance information received by the current node is synchronized from another node, it will not continue to be synchronized to other nodes to avoid "broadcast effect", resulting in an endless loop
if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
return;
}
for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
// If the url represents this host, do not replicate to yourself.
if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
continue; } replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node); }}finally{ tracer.stop(); }}Copy the code
private void replicateInstanceActionsToPeers(Action action, String appName, String id, InstanceInfo info, InstanceStatus newStatus, PeerEurekaNode node) {
try {
InstanceInfo infoFromRegistry = null;
CurrentRequestVersion.set(Version.V2);
switch (action) {
case Cancel:
node.cancel(appName, id);
break;
case Heartbeat:
InstanceStatus overriddenStatus = overriddenInstanceStatusMap.get(id);
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.heartbeat(appName, id, infoFromRegistry, overriddenStatus, false);
break;
case Register:
node.register(info);
break;
case StatusUpdate:
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.statusUpdate(appName, id, newStatus, infoFromRegistry);
break;
case DeleteStatusOverride:
infoFromRegistry = getInstanceByAppAndId(appName, id, false);
node.deleteStatusOverride(appName, id, infoFromRegistry);
break; }}catch (Throwable t) {
logger.error("Cannot replicate information to {} for action {}", node.getServiceUrl(), action.name(), t); }}Copy the code
Let’s look at the registration action corresponding method