zoukankan      html  css  js  c++  java
  • springcloud----eureka源码探索

    注册中心理解

    ​ 注册中心到底是什么?从学习微服务的时候就一直在想,又有什么作用?从开始的单机服务器,到多台服务器的协同调用。渐渐地明白了注册中心的重要性。

    ​ 在小型项目中,用单台服务器完全可以满足需求;再大一点的项目,单台服务器可以通过扩展服务器的配置满足需求,直至到服务器的瓶颈;当超过了单台服务器的瓶颈后,可以使用多个服务器的请求来进行协同。

    ​ 因为spring-cloud使用的是http请求来进行多服务器的协同合作,这里也只用http来进行说明。当我们通过http请求进行多个服务器来进行合作的时候,可能出现被请求的服务器宕机尔无响应,可能出现被请求的服务器服务器线程满了无法处理请求等,这时候就体现到了注册中心的重要性了。

    ​ eureka作为注册中心之一,只是将将每台服务器的信息的存入到eureka-server中,通过eureka-client来连接eureka-server,其中所有的请求都是通过http来完成。当需要远程调用的时候,直接获取client中存放的需要调用的服务器地址,实现远程调用即可。

    eureka配置启动解析

    @EnableEurekaServer
    //次注解只是向容器中注入一个Bean
    //EurekaServerMarkerConfiguration类中的
    
    @Bean
        public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
            return new EurekaServerMarkerConfiguration.Marker();
        }
    
    //在通过eureka-server的jar包中的spring.factory文件的配置
    //org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    //org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
    //启动eureka服务器。
    //EurekaServerAutoConfiguration中,
    //配置了http的入口controlller
    @Bean
        @ConditionalOnProperty(
            prefix = "eureka.dashboard",
            name = {"enabled"},
            matchIfMissing = true
        )
        public EurekaController eurekaController() {
            return new EurekaController(this.applicationInfoManager);
        }
    
    //配置了实例注册器,内部维护的就是eureka-client的信息
    @Bean
        public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
            this.eurekaClient.getApplications();
            return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
        }
    
    //jersy应用,eureka-client可以通过http请求,修改eureka维护的注册信息。
    //jersy也是一个web框架,专注于restFull,eureka作为注册中心将会使用到两个web框架
    //一个webMvc提供dashboar,一个jersy提供了以http请求方式维护注册信息
    @Bean
        public Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {
            ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);
            provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
            provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
            Set<Class<?>> classes = new HashSet();
            String[] var5 = EUREKA_PACKAGES;
            int var6 = var5.length;
    
            for(int var7 = 0; var7 < var6; ++var7) {
                String basePackage = var5[var7];
                Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
                Iterator var10 = beans.iterator();
    
                while(var10.hasNext()) {
                    BeanDefinition bd = (BeanDefinition)var10.next();
                    Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader());
                    classes.add(cls);
                }
            }
    
            Map<String, Object> propsAndFeatures = new HashMap();
            propsAndFeatures.put("com.sun.jersey.config.property.WebPageContentRegex", "/eureka/(fonts|images|css|js)/.*");
            DefaultResourceConfig rc = new DefaultResourceConfig(classes);
            rc.setPropertiesAndFeatures(propsAndFeatures);
            return rc;
        }
    //等等还有许多bean,暂不一一说明
    

    dashboard控制台

    ​ 我们通过访问dashboard面板是通过两个请求完成的,一种是EurekaController 提供的请求为“/”的资源,还有一个就是jersy提供的数据"eureka/status"的资源提供数据,返回dashboard面板页面。我们可以自己在EurekaController的资源和ApplicationResource的eureka/status中打断点,查看返回的结果来进行验证,不得不说设计的很漂亮,需要找半天。有兴趣的人可以自己看看jersy开了多少个web接口,自己请求看看分别有哪些东西。jersy的资源都在eureka-core的com.netflix.eureka.resources包中。

    注册实例

    ​ 实例注册是jersy提供的一个比较重要的接口,在applicationResource中,为一个post请求,在这个其你去里面可以看见eureka-client传给服务器有哪些信息,通过这些信息,eureka可以提供哪些功能。

    @POST
        @Consumes({"application/json", "application/xml"})
        public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) {
            logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
            if (this.isBlank(info.getId())) {
                return Response.status(400).entity("Missing instanceId").build();
            } else if (this.isBlank(info.getHostName())) {
                return Response.status(400).entity("Missing hostname").build();
            } else if (this.isBlank(info.getIPAddr())) {
                return Response.status(400).entity("Missing ip address").build();
            } else if (this.isBlank(info.getAppName())) {
                return Response.status(400).entity("Missing appName").build();
            } else if (!this.appName.equals(info.getAppName())) {
                return Response.status(400).entity("Mismatched appName, expecting " + this.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();
            } else {
                DataCenterInfo dataCenterInfo = info.getDataCenterInfo();
                if (dataCenterInfo instanceof UniqueIdentifier) {
                    String dataCenterInfoId = ((UniqueIdentifier)dataCenterInfo).getId();
                    if (this.isBlank(dataCenterInfoId)) {
                        boolean experimental = "true".equalsIgnoreCase(this.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();
                        }
    
                        if (dataCenterInfo instanceof AmazonInfo) {
                            AmazonInfo amazonInfo = (AmazonInfo)dataCenterInfo;
                            String effectiveId = amazonInfo.get(MetaDataKey.instanceId);
                            if (effectiveId == null) {
                                amazonInfo.getMetadata().put(MetaDataKey.instanceId.getName(), info.getId());
                            }
                        } else {
                            logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());
                        }
                    }
                }
    
                this.registry.register(info, "true".equals(isReplication));
                return Response.status(204).build();
            }
        }
    
    
    //abstractInstanceRegistry类中注册功能
        public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
            try {
                this.read.lock();
                Map<String, Lease<InstanceInfo>> gMap = (Map)this.registry.get(registrant.getAppName());
                EurekaMonitors.REGISTER.increment(isReplication);
                if (gMap == null) {
                    ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap();
                    gMap = (Map)this.registry.putIfAbsent(registrant.getAppName(), gNewMap);
                    if (gMap == null) {
                        gMap = gNewMap;
                    }
                }
    
                Lease<InstanceInfo> existingLease = (Lease)((Map)gMap).get(registrant.getId());
                if (existingLease != null && existingLease.getHolder() != null) {
                    Long existingLastDirtyTimestamp = ((InstanceInfo)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 = (InstanceInfo)existingLease.getHolder();
                    }
                } else {
                    synchronized(this.lock) {
                        if (this.expectedNumberOfClientsSendingRenews > 0) {
                            ++this.expectedNumberOfClientsSendingRenews;
                            this.updateRenewsPerMinThreshold();
                        }
                    }
    
                    logger.debug("No previous lease information found; it is new registration");
                }
    
                Lease<InstanceInfo> lease = new Lease(registrant, leaseDuration);
                if (existingLease != null) {
                    lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
                }
    
                ((Map)gMap).put(registrant.getId(), lease);
                synchronized(this.recentRegisteredQueue) {
                    this.recentRegisteredQueue.add(new Pair(System.currentTimeMillis(), registrant.getAppName() + "(" + registrant.getId() + ")"));
                }
    
                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 (!this.overriddenInstanceStatusMap.containsKey(registrant.getId())) {
                        logger.info("Not found overridden id {} and hence adding it", registrant.getId());
                        this.overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());
                    }
                }
    
                InstanceStatus overriddenStatusFromMap = (InstanceStatus)this.overriddenInstanceStatusMap.get(registrant.getId());
                if (overriddenStatusFromMap != null) {
                    logger.info("Storing overridden status {} from map", overriddenStatusFromMap);
                    registrant.setOverriddenStatus(overriddenStatusFromMap);
                }
    
                InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(registrant, existingLease, isReplication);
                registrant.setStatusWithoutDirty(overriddenInstanceStatus);
                if (InstanceStatus.UP.equals(registrant.getStatus())) {
                    lease.serviceUp();
                }
    
                registrant.setActionType(ActionType.ADDED);
                this.recentlyChangedQueue.add(new AbstractInstanceRegistry.RecentlyChangedItem(lease));
                registrant.setLastUpdatedTimestamp();
                this.invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
                logger.info("Registered instance {}/{} with status {} (replication={})", new Object[]{registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication});
            } finally {
                this.read.unlock();
            }
    
        }
    

    ​ 这个里面实际就是维护了一个Map集合,通过http请求,将需要客户端的信息放入到Map中,如此简单。还记得开始学的时候,就有人说过eureka的界面很丑,git中曾有大佬自己做了个eureka页面。对比下nacos中的手动剔除实力等功能,想想只用提出一个map实例即可。

    实例自动剔除

    ​ eureka实例自动剔除,至于在那个类中定义的定时任务,一时上我也忘了。但是定时任务是30s调用一次这个方法,调用的方法在PeerAwareInstanceRegistryImpl类中的

    public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
            this.expectedNumberOfClientsSendingRenews = count;
            this.updateRenewsPerMinThreshold();
            logger.info("Got {} instances from neighboring DS node", count);
            logger.info("Renew threshold is: {}", this.numberOfRenewsPerMinThreshold);
            this.startupTime = System.currentTimeMillis();
            if (count > 0) {
                this.peerInstancesTransferEmptyOnStartup = false;
            }
    
            Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
            boolean isAws = Name.Amazon == selfName;
            if (isAws && this.serverConfig.shouldPrimeAwsReplicaConnections()) {
                logger.info("Priming AWS connections for all replicas..");
                this.primeAwsReplicas(applicationInfoManager);
            }
    
            logger.info("Changing status to UP");
            applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
            super.postInit();
        }
    
    	学习使我快乐,希望对你也有所帮助,一起将这份快乐传递。
  • 相关阅读:
    什么样的项目适合docker部署,docker应用场景
    算法学习导图+经典排序算法PHP实现
    Xmind激活:亲测有效
    小细节1:mysql数据库中的主键删除后出现自定义主键约束
    java笔试题:找出3~999的水仙花数的三种实现方式
    java笔试题:利用冒泡排序算法找出int类型数组当中最大的数字
    java笔试题:判断一个3~100之间的所有的素数?
    java笔试题:随机生成一个4位数字的年号,判断是否是闰年?
    java笔试题:判断一个小写字母是元音字母还是辅音字母?
    安装Maven方法
  • 原文地址:https://www.cnblogs.com/theStone/p/15024107.html
Copyright © 2011-2022 走看看