SpringCloudEureka服务注册流程图及源码

@EnableEurekaServer是个开关在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
实现原理:
eureka的实现原理是基于Jersey框架过滤器拦截,节点间的通信采用HTTP,服务器接受请求后,拦截request,获取对应的执行操作调用不同的处理逻辑。
在这里插入图片描述
核心源码

  @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();
        }
    }

在这里插入图片描述
流程
服务端启动
节点启动发送http请求到服务端
服务端拦截后找到对应方法addInstance
调用register
查询注册表该节点信息
如不存在创建
判断注册冲突
创建该节点注册信息
保存到注册表
清缓存

猜你喜欢

转载自blog.csdn.net/GiantCrocodile/article/details/124842867