【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-client\src\main\resources\META-INF\spring.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对象

 

转载于:https://www.cnblogs.com/lodor/p/7580887.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值