SpringCloudEureka service registration flowchart and source code

@EnableEurekaServer is a switchinsert image description here
insert image description here

insert image description here
Implementation principle:
The implementation principle of eureka is based on the filter interception of the Jersey framework. The communication between nodes adopts HTTP. After the server accepts the request, it intercepts the request and obtains the corresponding execution operation to call different processing logic.
insert image description here
core source code

  @POST
    @Consumes({
    
    "application/json", "application/xml"})
    public Response addInstance(InstanceInfo info,
                                @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    
    
        //isReplication参数判断当前请求是eureka多节点同步还是单节点注册,TRUE为集群,FALSE为单节点注册请求
        logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
        // 验证请求的必要字段(某个微服务的注册的必要条件)
        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();
        }
        //核心代码
        registry.register(info, "true".equals(isReplication));
        return Response.status(204).build();  // 204 to be backwards compatible
    }

    /**
     * @Description: 注册该节点信息,并将该信息同步到其他同等eureka节点 (职责单一原则)
     * @Author: PABLO
     * @Date: 2022/5/18 12:12
     * @Params: [info, isReplication是否需要复制]
     * @Return: void
     **/
    public void register(final InstanceInfo info, final boolean isReplication) {
    
    
        //默认租约90秒(过期时间,eurekaServer  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(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
    }


    //注册表数据结构 map嵌套视为了满足集群的存储
    //如user服务集群,外侧的String保存该user集群的名称AppName,
    // 内侧String保存每个节点的id如DESKTOP-523JB40:eureka-client-user:9092,而后对应一个Lease<InstanceInfo>包装
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

    /**
     * @Description: 执行注册
     * @Author: PABLO
     * @Date: 2022/5/18 12:34
     * @Params: [registrant 注册节点信息, leaseDuration 租约,即续约时间, isReplication 是否同步]
     * @Return: void
     **/
    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
    
    
        //读锁,虽然可保证代码块的读写顺序,但registry还是共享的
        read.lock();
        try {
    
    
            //查询缓存(注册表)获取对应集群信息,AppName即我们设置的ymal中的spring.application.name=eureka-client-user
            Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
            REGISTER.increment(isReplication);
            //不存在,如首次进入
            if (gMap == null) {
    
    
                //创建map
                final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
                //这是concurrentHashMap操作,如对应位置有值,返回对应位置的值,如果没有,放入,返回null
                gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
                //说明该位置是空的,说明该节点还未注册
                if (gMap == null) {
    
    
                    //将新节点替换null,下面会完善gMap即当前节点的具体信息
                    gMap = gNewMap;
                }
            }
            //根据节点id(DESKTOP-523JB40:eureka-client-user:9092)获取对应节点信息
            //如首次进入应该是空的,如并发,可能会注册冲突
            Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
            if (existingLease != null && (existingLease.getHolder() != null)) {
    
    
                //获取冲突的最后活跃操作时间
                Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
                Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
                logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
                //如果已存在的节点更加活跃,即这边在注册时候,已注册的节点又一次发来了一次连接,那就使用已注册的
                //那个节点活跃用那个
                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 {
    
    
                //没有租约信息,说明是新的注册节点
                synchronized (lock) {
    
    
                    if (this.expectedNumberOfClientsSendingRenews > 0) {
    
    
                        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
                        updateRenewsPerMinThreshold();
                    }
                }
                logger.debug("No previous lease information found; it is new registration");
            }
            //创建新的续约对象
            //InstanceInfo对象保存了该节点的所有信息,续约、剔除等相关节点的操作都是操作此对象
            Lease<InstanceInfo> lease = new Lease<>(registrant, leaseDuration);
            if (existingLease != null) {
    
    
                lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
            }
            //将此节点放入该节点集群的map集合中(map只有一个节点就是单机)
            gMap.put(registrant.getId(), lease);
            //........
            //.........
            //清除缓存
           invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
			//........
        } finally {
    
    
            read.unlock();
        }
    }

    /**
     * @Description: eureka集群信息同步
     * @Author: PABLO
     * @Date: 2022/5/18 14:45
     * @Params: [action, appName, id, info, newStatus, isReplication]
     * @Return: void
     **/
    private void replicateToPeers(PeerAwareInstanceRegistryImpl.Action action, String appName, String id,
                                  InstanceInfo info /* optional */,
                                  InstanceInfo.InstanceStatus newStatus /* optional */, boolean isReplication) {
    
    
        Stopwatch tracer = action.getTimer().start();
        try {
    
    
            if (isReplication) {
    
    
                numberOfReplicationsLastMin.increment();
            }
            //判断重复复制
            if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
    
    
                return;
            }
            //复制
            for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
    
    
                //跳过自己
                if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
    
    
                    continue;
                }
                replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
            }
        } finally {
    
    
            tracer.stop();
        }
    }
    /**
     * @Description: 执行复制操作
     * @Author: PABLO
     * @Date: 2022/5/18 14:47
     * @Params: [action, appName, id, info, newStatus, node]
     * @Return: void
     **/
    private void replicateInstanceActionsToPeers(PeerAwareInstanceRegistryImpl.Action action, String appName,
                                                 String id, InstanceInfo info, InstanceInfo.InstanceStatus newStatus,
                                                 PeerEurekaNode node) {
    
    
        try {
    
    
            InstanceInfo infoFromRegistry;
            CurrentRequestVersion.set(Version.V2);
            //判断状态进行对应操作
            switch (action) {
    
    
                case Cancel:
                    node.cancel(appName, id);
                    break;
                case Heartbeat:
                    InstanceInfo.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);
        } finally {
    
    
            CurrentRequestVersion.remove();
        }
    }

insert image description here
Process
The server starts,
the node starts and sends an http request to the server.
After the server intercepts, it finds the corresponding method addInstance
and calls register
to query the information of the node in the registry.
If there is no creation
, determine the registration conflict.
Create the registration information of the node,
save it in the registry,
and clear the cache

Guess you like

Origin blog.csdn.net/GiantCrocodile/article/details/124842867