• 【spring cloud】源码分析(一)


    概述

           从服务发现注解

       @EnableDiscoveryClient入手,剖析整个服务发现与注册过程

    一,spring-cloud-common包

           针对服务发现,本jar包定义了

           DiscoveryClient 接口

    public interface DiscoveryClient {
        /**
         * A human readable description of the implementation, used in HealthIndicator
         * @return the description
         */
        String description();
        /**
         * @deprecated use the {@link org.springframework.cloud.client.serviceregistry.Registration} bean instead
         *
         * @return ServiceInstance with information used to register the local service
         */
        @Deprecated
        ServiceInstance getLocalServiceInstance();
        /**
         * Get all ServiceInstances associated with a particular serviceId
         * @param serviceId the serviceId to query
         * @return a List of ServiceInstance
         */
        List<ServiceInstance> getInstances(String serviceId);
        /**
         * @return all known service ids
         */
        List<String> getServices();
    
    }

            EnableDiscoveryClient注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(EnableDiscoveryClientImportSelector.class)     //关键在这句话
    public @interface EnableDiscoveryClient {
        /**
         * If true, the ServiceRegistry will automatically register the local server.
         */
        boolean autoRegister() default true;
    }
    @Import注解:支持导入普通的java类,并将其声明成一个bean
    现在看EnableDiscoveryClientImportSelector类实现
    @Order(Ordered.LOWEST_PRECEDENCE - 100)     //指定实例化bean的顺序
    public class EnableDiscoveryClientImportSelector
            extends SpringFactoryImportSelector<EnableDiscoveryClient> {
    
        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            String[] imports = super.selectImports(metadata);
    
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                    metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
    
            boolean autoRegister = attributes.getBoolean("autoRegister");
    
            if (autoRegister) {
                List<String> importsList = new ArrayList<>(Arrays.asList(imports));
                importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
                imports = importsList.toArray(new String[0]);
            }
    
            return imports;
        }
    EnableDiscoveryClientImportSelector类继承SpringFactoryImportSelector类,该类是重点,如下:
    public abstract class SpringFactoryImportSelector<T>
            implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
    
        private ClassLoader beanClassLoader;
    
        private Class<T> annotationClass;
    
        private Environment environment;
    
        private final Log log = LogFactory.getLog(SpringFactoryImportSelector.class);
    
        @SuppressWarnings("unchecked")
        protected SpringFactoryImportSelector() {
            this.annotationClass = (Class<T>) GenericTypeResolver
                    .resolveTypeArgument(this.getClass(), SpringFactoryImportSelector.class);
        }
    
        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            if (!isEnabled()) {
                return new String[0];
            }
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                    metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
    
            Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
                    + metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
    
            // Find all possible auto configuration classes, filtering duplicates    重点在这个地方
            List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
                    .loadFactoryNames(this.annotationClass, this.beanClassLoader)));
    
            if (factories.isEmpty() && !hasDefaultFactory()) {
                throw new IllegalStateException("Annotation @" + getSimpleName()
                        + " found, but there are no implementations. Did you forget to include a starter?");
            }
    
            if (factories.size() > 1) {
                // there should only ever be one DiscoveryClient, but there might be more than
                // one factory
                log.warn("More than one implementation " + "of @" + getSimpleName()
                        + " (now relying on @Conditionals to pick one): " + factories);
            }
    
            return factories.toArray(new String[factories.size()]);
        }

    SpringFactoriesLoader调用loadFactoryNames其实加载META-INF/spring.factories下的class。

    spring-cloud-netflix-eureka-clientsrcmain esourcesMETA-INFspring.factories中配置:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,
    org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,
    org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,
    org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
    
    org.springframework.cloud.bootstrap.BootstrapConfiguration=
    org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
    
    org.springframework.cloud.client.discovery.EnableDiscoveryClient=
    org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

    该类调用了DeferredImportSelector接口,即ImportSelector接口

    ,继承了selectImport方法,关于ImportSelector的具体作用,参考下面链接

     selectImport详解

      对于selectImport的调用,是在spring context 包中的ConfigurationClassParser进行解析

    先流程走到EurekaClientAutoConfiguration类与EurekaDiscoveryClientConfiguration类

          EurekaClientAutoConfiguration详解

      源码如下
    @Configuration      
    @EnableConfigurationProperties   
    @ConditionalOnClass(EurekaClientConfig.class)   // 该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类;

    @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) //属性必须存在,才解析该类
    @AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
            CommonsClientAutoConfiguration.class })
    @AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")
    public class EurekaClientAutoConfiguration {
    
        @Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
        int nonSecurePort;
    
        @Value("${management.port:${MANAGEMENT_PORT:${server.port:${SERVER_PORT:${PORT:8080}}}}}")
        int managementPort;
    
        @Value("${eureka.instance.hostname:${EUREKA_INSTANCE_HOSTNAME:}}")
        String hostname;
    
        @Autowired
        ConfigurableEnvironment env;     //环境上下文,即配置文件相关的内容
     
        @Bean
        public HasFeatures eurekaFeature() {
            return HasFeatures.namedFeature("Eureka Client", EurekaClient.class);
        }
        // EurekaClientConfigBean:  服务注册类配置,如指定注册中心,以及定义了各种超时时间,比如下线超时时间,注册超时时间等
    // 这些注册类配置,以eureka.client为前缀 @Bean @ConditionalOnMissingBean(value
    = EurekaClientConfig.class, search = SearchStrategy.CURRENT) public EurekaClientConfigBean eurekaClientConfigBean() { EurekaClientConfigBean client = new EurekaClientConfigBean(); if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) { // We don't register during bootstrap by default, but there will be another // chance later. client.setRegisterWithEureka(false); } return client; } // EurekaInstanceConfigBean: 服务实例类配置 instance 实例配置 ,包括appname,instanceId,主机名等
    // 1:元数据:实例元数据的配置,比如服务名称,实例名称,实例ip,端口,安全通信端口,非安全通信端口,心跳间隔等 此类信息会包装成InstanceInfo 然后传递到服务中心
    // 以eureka.instance配置为前缀
    @Bean @ConditionalOnMissingBean(value
    = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT) public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) { EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils); instance.setNonSecurePort(this.nonSecurePort); instance.setInstanceId(getDefaultInstanceId(this.env)); if (this.managementPort != this.nonSecurePort && this.managementPort != 0) { if (StringUtils.hasText(this.hostname)) { instance.setHostname(this.hostname); } RelaxedPropertyResolver relaxedPropertyResolver = new RelaxedPropertyResolver(env, "eureka.instance."); String statusPageUrlPath = relaxedPropertyResolver.getProperty("statusPageUrlPath"); String healthCheckUrlPath = relaxedPropertyResolver.getProperty("healthCheckUrlPath"); if (StringUtils.hasText(statusPageUrlPath)) { instance.setStatusPageUrlPath(statusPageUrlPath); } if (StringUtils.hasText(healthCheckUrlPath)) { instance.setHealthCheckUrlPath(healthCheckUrlPath); } String scheme = instance.getSecurePortEnabled() ? "https" : "http"; instance.setStatusPageUrl(scheme + "://" + instance.getHostname() + ":" + this.managementPort + instance.getStatusPageUrlPath()); instance.setHealthCheckUrl(scheme + "://" + instance.getHostname() + ":" + this.managementPort + instance.getHealthCheckUrlPath()); } return instance; } @Bean public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) { return new EurekaDiscoveryClient(config, client); //注册服务发现客户端类成bean } @Bean @ConditionalOnMissingBean(value = DiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT) public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() { return new MutableDiscoveryClientOptionalArgs(); }

    spring-cloud-netflix-eureka-client的EurekaDiscoveryClilent类只是对我们注解用到的DiscoveryClient接口的实现, 该类中的EurekaClient接口变量才是真正对服务发现实现,即Eureka-client中的EurekaClient接口实现类DiscoveryClient才是真正对发现服务进行了实现

    DiscoveryClient类的实现内容:

       1:向Eureka server 注册服务实例

       2:向Eureka server 服务租约

       3:当服务关闭期间,向Eureka server 取消租约

       4:查询Eureka server 中的服务实例列表

       Eureka client 还需要配置一个Eureka server 的url列表

       DiscoveryClient类服务注册关键实现:

    private void initScheduledTasks() {
            int renewalIntervalInSecs;
            int expBackOffBound;
            if(this.clientConfig.shouldFetchRegistry()) {
                renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds();
                expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
                this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
            }
    
            if(this.clientConfig.shouldRegisterWithEureka()) {
                renewalIntervalInSecs = this.instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
                expBackOffBound = this.clientConfig.getHeartbeatExecutorExponentialBackOffBound();
                logger.info("Starting heartbeat executor: renew interval is: " + renewalIntervalInSecs);
    this.scheduler.schedule(new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread(null)), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
                /* 下面这行启动一个定时任务,这个定时任务的执行在后面,作用是向服务端注册 */
    this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);
                this.statusChangeListener = new StatusChangeListener() {
                    public String getId() {
                        return "statusChangeListener";
                    }
    
                    public void notify(StatusChangeEvent statusChangeEvent) {
                        if(InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
                            DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
                        } else {
                            DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
                        }
    
                        DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
                    }
                };
                if(this.clientConfig.shouldOnDemandUpdateStatusChange()) {
                    this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);
                }
    
                this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
            } else {
                logger.info("Not registering with Eureka server per configuration");
            }
    
        }

    查看InstanceInfoReplicator类的实现,这个类是个线程类,查看里面的run方法

     this.discoveryClient.refreshInstanceInfo();
                    Long next = this.instanceInfo.isDirtyWithTime();
                    if(next != null) {
                        this.discoveryClient.register();
                        this.instanceInfo.unsetIsDirty(next.longValue());
                        var6 = false;
                    } else {
                        var6 = false;
                    }

    这个discoveryclient.register调用了http请求,实现了注册,传入参数是com.netflix.appinfo.instanceInfo对象

  • 相关阅读:
    大教堂和市集
    VB相关资源
    什么样的团队才是优秀的团队
    走过2007
    一种简单实用的错误码构建方法
    熊的困惑
    调测中的反思
    PowerPC852T的SMC初始化—串口通信
    Silverlight Listbox 取消全选的方法
    电脑读不出U盘,有一个黄色感叹号的解决方法
  • 原文地址:https://www.cnblogs.com/lodor/p/7580887.html
Copyright © 2020-2023  润新知