Eureka series (07) registered and active service offline

Eureka series (07) registered and active service offline

[TOC]

Spring Cloud Series catalog - Eureka articles

In the previous Eureka series (05) message broadcast in the message broadcast on Eureka source code is analyzed, after several articles will be a detailed analysis of local service registration, the initiative off the assembly line, the heartbeat renew automatically expire and other implementation mechanisms.

  • PeerAwareInstanceRegistryImpl Cluster is responsible for internal messaging.
  • AbstractInstanceRegistry Responsible for local service information management, which is the focus of attention after a few articles.
表 1: Eureka OPEN API
Resources Features url
ApplicationsResource To capture all or incremental service instance information GET /apps
GET /apps/delta
ApplicationResource 1. Obtain a single application information
2. Examples of registration information
GET /apps/{appName}
POST /apps/{appName}
InstanceResource CURD service instance:
1. Examples of information acquisition
2. The modified meta information service instance
delete instance information services offline
4 to send the heartbeat
GET /apps/{appName}/{id}
PUT /apps/{appName}/{id}/metadata
DELETE /apps/{appName}/{id}
PUT /apps/{appName}/{id}
InstancesResource Examples of information directly acquired according to example id GET /instances/{id}
PeerReplicationResource Within a cluster bulk data synchronization POST /peerreplication/batch
ServerInfoResource ??? POST /serverinfo/statusoverrides
StatusResource ??? GET /statusoverrides

Note: represents the name of the application or service id, representing the instance id. EG: HTTP: // localhost: 8080 / Eureka / Apps

1. Service registration

1.1 service instance registration process

Figure 1: Eureka service instance registered timing diagram
sequenceDiagram participant ApplicationResource participant PeerAwareInstanceRegistryImpl participant AbstractInstanceRegistry participant PeerEurekaNode note over ApplicationResource: POST: / euraka / apps / {appName} <br/> addInstance (instanceInfo, isReplication) ApplicationResource - >> PeerAwareInstanceRegistryImpl: Registration Request: register (instanceInfo, isReplication) PeerAwareInstanceRegistryImpl - >> AbstractInstanceRegistry: 1. local data update: register (instanceInfo, leaseDuration, isReplication) loop synchronized to the other nodes Eureka Server PeerAwareInstanceRegistryImpl - >> PeerAwareInstanceRegistryImpl: 2.1 data synchronization: replicateInstanceActionsToPeers PeerAwareInstanceRegistryImpl - >> PeerEurekaNode: 2.2 register (instanceInfo) -> POST : / euraka / apps / {appName} end

Summary: Eureka Web using the Jersey container service registration request is register entry method ApplicationResource, the path of the request is POST:/euraka/apps/{appName}.

1.2 ApplicationResource

// ApplicationResource HTTP请求入口
@POST
@Consumes({"application/json", "application/xml"})
public Response addInstance(InstanceInfo info,
	@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    ... // 数据检验
    registry.register(info, "true".equals(isReplication));
    return Response.status(204).build();  // 204 to be backwards compatible
}

Summary: ApplicationResource main entrance is parametric test, the main logic are delegated to the PeerAwareInstanceRegistryImpl completed.

Note: isReplication argument, if it is the client's request was false, expressed the need for the news broadcast to other servers. If the cluster message broadcast was true, that is no longer the need to continue broadcasting, which will cause circulation problems broadcast.

// PeerAwareInstanceRegistryImpl 默认注册器实现
@Override
public void register(final InstanceInfo info, final boolean isReplication) {
    // 租约的过期时间,默认90秒
    int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
    if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
        // 如果客户端自定义了,那么以客户端为准
        leaseDuration = info.getLeaseInfo().getDurationInSecs();
    }
    // 本地注册
    super.register(info, leaseDuration, isReplication);
    // 消息广播
    replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

Summary: PeerAwareInstanceRegistryImpl this class should be very familiar, is responsible for internal communications between the cluster, its parent AbstractInstanceRegistry is responsible for local service information management, but also the research focus of this article.

1.3 AbstractInstanceRegistry

In Eureka, the service registration information stored in memory, the data structure ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry, Map nested layers, the outer layer is appName Key, Key memory is InstanceId.

private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
    try {
        read.lock();
        // 1. 获取该服务对应的所有服务实例,如果不存在就创建一个新的Map
        Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
        REGISTER.increment(isReplication);
        if (gMap == null) {
            final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
            gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
            if (gMap == null) {
                gMap = gNewMap;
            }
        }
        
        // 2. 两种情况:一是实例已经注册,二是实例没有注册
        Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
        // 2.1 实例已经注册,就需要PK,PK原则:谁最后一次更新就是谁赢
        //     也就是说如果已经注册的实例最近更新了,就不用重新更新了
        if (existingLease != null && (existingLease.getHolder() != null)) {
            Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
            Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
            // 已经注册的实例PK赢了
            if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
                registrant = existingLease.getHolder();
            }
        // 2.2 没有注册,很好处理。更新注册的实例个数
        } else {
            synchronized (lock) {
                if (this.expectedNumberOfClientsSendingRenews > 0) {
                    this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
                    updateRenewsPerMinThreshold();
                }
            }
        }
        
        // 3. 更新注册信息(核心步骤)
        Lease<InstanceInfo> lease = new Lease<InstanceInfo>(registrant, leaseDuration);
        if (existingLease != null) {
            lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
        }
        // (核心步骤)
        gMap.put(registrant.getId(), lease);
        // 添加到最近的注册队列里面去,以时间戳作为Key,名称作为value,主要是为了运维界面的统计数据
        synchronized (recentRegisteredQueue) {
            recentRegisteredQueue.add(new Pair<Long, String>(
                System.currentTimeMillis(),
                registrant.getAppName() + "(" + registrant.getId() + ")"));
        }
        
        // 4. 更新实例状态 InstanceStatus
        if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {
            if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {
                overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());
            }
        }
        InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());
        if (overriddenStatusFromMap != null) {
            registrant.setOverriddenStatus(overriddenStatusFromMap);
        }

        InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);
        registrant.setStatusWithoutDirty(overriddenInstanceStatus);

        if (InstanceStatus.UP.equals(registrant.getStatus())) {
            lease.serviceUp();
        }
        
        // 5. 清理缓存等善后工作
        registrant.setActionType(ActionType.ADDED);
        // 租约变更记录队列,记录了实例的每次变化, 用于注册信息的增量获取
        recentlyChangedQueue.add(new RecentlyChangedItem(lease));
        registrant.setLastUpdatedTimestamp();
        // 清理缓存 ,传入的参数为key
        invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
    } finally {
        read.unlock();
    }
}

Summary: The first three steps of logic is very clear, the purpose is to update the instance information in memory, just need to pay attention to the case of the example already exists about the need for PK, in principle, that is, who is who won the last update.

Examples of status update service fourth step, OverriddenStatus reference InstanceInfo role in OverriddenStatus

Every five steps to clear the cache and other rehabilitation work. For now, to gMap.put(registrant.getId(), lease)this point enough.

2. initiative off the assembly line

OPEN API corresponding service is offline DELETE /apps/{appName}/{id}

protected boolean internalCancel(String appName, String id, boolean isReplication) {
    try {
        read.lock();
        CANCEL.increment(isReplication);
        // 1. 清空registry中注册的实例信息(核心步骤)
        Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
        Lease<InstanceInfo> leaseToCancel = null;
        if (gMap != null) {
            leaseToCancel = gMap.remove(id);
        }
        // 2. 添加到 recentCanceledQueue 队列中
        synchronized (recentCanceledQueue) {
            recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
        }
        InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
        if (leaseToCancel == null) {
            CANCEL_NOT_FOUND.increment(isReplication);
            return false;
        } else {
            // 3. 和注册时一样,也要做一下清除缓存等善后工作
            leaseToCancel.cancel();
            InstanceInfo instanceInfo = leaseToCancel.getHolder();
            String vip = null;
            String svip = null;
            if (instanceInfo != null) {
                instanceInfo.setActionType(ActionType.DELETED);
                recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
                instanceInfo.setLastUpdatedTimestamp();
                vip = instanceInfo.getVIPAddress();
                svip = instanceInfo.getSecureVipAddress();
            }
            invalidateCache(appName, vip, svip);
            return true;
        }
    } finally {
        read.unlock();
    }
}

Summary: registered and active downline logical service is still very clear. ** For now, to gMap.remove(id)this point enough. ** As for the future details of the real use Eureka continue in-depth study.


The intentions of recording a little bit every day. Perhaps the content is not important, but the habit is very important!

Guess you like

Origin www.cnblogs.com/binarylei/p/11618566.html