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 statusescom.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