Loading

Eureka源码解析

SpringBoot中的自动装载

(1)ImportSelector

ImportSelector接口是Spring导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)中起到了决定性的作用。当在@Confifiguration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称都定义为bean。
public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}
DeferredImportSelector接口继承ImportSelector,他和ImportSelector的区别在于装载bean的时机上,DeferredImportSelector需要等所有的@Confifiguration都执行完毕后才会进行装载
public interface DeferredImportSelector extends ImportSelector {
 //...省略
}
接下来我们写一个小例子,看下ImportSelector的用法

1)定义Bean对象

public class User {
 private String username;
 private Integer age;
 //省略..
}

2)定义配置类Confifiguration

//定义一个configuration ,注意这里并没有使用spring注解,spring扫描的时候并不会装载该类
public class UserConfiguration {
 @Bean
 public User getUser() {
 return new User("张三",18);
 }
}

3 ) 定义ImportSelector

public class UserImportSelector implements ImportSelector {
 @Override
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取配置类名称
 return new String[]{UserConfiguration.class.getName()};
 }
}

4) 定义EnableXXX注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(UserImportSelector.class)
public @interface EnableUserBean {
}

5 ) 测试

/**
* 通过在类上声明@EnableUserBean,会自动的加载所有对象
*/
@EnableUserBean
public class TestApplication {
 public static void main(String[] args) {
 AnnotationConfigApplicationContext applicationContext = new
AnnotationConfigApplicationContext(TestApplication.class);
 User user = applicationContext.getBean(User.class);
 System.out.println(user);
 }
}
由此可见,HelloWorldConfifiguration对象并没有使用Spring的对象对象创建注解声明(@Controller,@Service,@Repostiroty),而是使用编程的方式动态的载入bean。
这个接口在哪里调用呢?我们可以来看一下ConfifigurationClassParser这个类的processImports方法
private void processImports(ConfigurationClass configClass, SourceClass
currentSourceClass,
            Collection<SourceClass> importCandidates, boolean
checkForCircularImports) {
        if (importCandidates.isEmpty()) {
            return;
       }
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, 
this.importStack));
       }
else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) 
{ //对ImportSelector的处理
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to 
it to determine imports
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector =
BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, 
this.registry);
                        if (this.deferredImportSelectors != null && selector
instanceof DeferredImportSelector) { //如果为延迟导入处理
则加入集合当中
                            this.deferredImportSelectors.add(
                                    new
DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                       }
                        else { //根据ImportSelector方法
的返回值来进行递归操作
                            String[] importClassNames =
selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses =
asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, 
importSourceClasses, false);
                       }
                   }
                    else if
(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar -
>
                        // delegate to it to register additional bean 
definitions
                        Class<?> candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                BeanUtils.instantiateClass(candidateClass, 
ImportBeanDefinitionRegistrar.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                registrar, this.environment, 
this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, 
currentSourceClass.getMetadata());
                   }
                    else { // 如果当前的类既不是
ImportSelector也不是ImportBeanDefinitionRegistar就进行@Configuration的解析处理
                        // Candidate class not an ImportSelector or 
ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), 
candidate.getMetadata().getClassName());
                       
processConfigurationClass(candidate.asConfigClass(configClass));
                   }
               }
           }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
           }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration 
class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
           }
            finally {
                this.importStack.pop();
           }
       }
   }
在这里我们可以看到ImportSelector接口的返回值会递归进行解析,把解析到的类全名按照@Confifiguration进行处理

(2)springBoot自动装载

SpringBoot开箱即用的特点,很大程度上归功于ImportSelector。接下来我们看下springBoot是如何在spring的基础上做扩展的。
在SpringBoot中最重要的一个注解SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
 //...
}
在SpringBootApplication注解中声明了一个 @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
在EnableAutoConfifiguration中通过Import引入了SpringBoot定义的AutoConfigurationImportSelector
这个类内容比较多,我们只需看下最主要的逻辑代码即可
public class AutoConfigurationImportSelector
 implements DeferredImportSelector, BeanClassLoaderAware, 
ResourceLoaderAware,
 BeanFactoryAware, EnvironmentAware, Ordered {
 @Override
 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 if (!isEnabled(annotationMetadata)) {
 return NO_IMPORTS;
 }
 AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader
 .loadMetadata(this.beanClassLoader);
 //主要逻辑在getAutoConfigurationEntry这个方法
 AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(
 autoConfigurationMetadata, annotationMetadata);
 return
StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
 }
 protected AutoConfigurationEntry getAutoConfigurationEntry(
 AutoConfigurationMetadata autoConfigurationMetadata,
 AnnotationMetadata annotationMetadata) {
 if (!isEnabled(annotationMetadata)) {
 return EMPTY_ENTRY;
 }
 AnnotationAttributes attributes = getAttributes(annotationMetadata);
 //通过getCandidateConfigurations方法获取所有需要加载的bean
 List<String> configurations =
getCandidateConfigurations(annotationMetadata,
 attributes);
 //去重处理
 configurations = removeDuplicates(configurations);
 //获取不需要加载的bean,这里我们可以通过spring.autoconfigure.exclude人为配置
 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
 checkExcludedClasses(configurations, exclusions);
 configurations.removeAll(exclusions);
 configurations = filter(configurations, autoConfigurationMetadata);
 //发送事件,通知所有的AutoConfigurationImportListener进行监听
fireAutoConfigurationImportEvents(configurations, exclusions);
 return new AutoConfigurationEntry(configurations, exclusions);
 }
 //这里是获取bean渠道的地方,重点看SpringFactoriesLoader#loadFactoryNames
 protected List<String> getCandidateConfigurations(AnnotationMetadata
metadata,
 AnnotationAttributes attributes) {
 //这里的getSpringFactoriesLoaderFactoryClass()最终返回
EnableAutoConfiguration.class
 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
 Assert.notEmpty(configurations,
 "No auto configuration classes found in METAINF/spring.factories. If you "
 + "are using a custom packaging, make sure that file is 
correct.");
 return configurations;
 }
}
从上面的逻辑可以看出,最终获取bean的渠道在SpringFactoriesLoader.loadFactoryNames
public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/spring.factories";
    private static final Log logger =
LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
new ConcurrentReferenceHashMap();
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        //通过factoryClassName获取相应的bean全称
 //上面传入的factoryClass是EnableAutoConfiguration.class
        return
(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 
Collections.emptyList());
   }
    private static Map<String, List<String>> loadSpringFactories(@Nullable
ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
       } else {
            try {
                //获取工程中所有META-INF/spring.factories文件,将其中的键值组合成Map
                Enumeration<URL> urls = classLoader != null ?
classLoader.getResources("META-INF/spring.factories") : 
ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
 UrlResource resource = new UrlResource(url);
                    Properties properties =
PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName =
((String)entry.getKey()).trim();
                        String[] var9 =
StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;
                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                       }
                   }
               }
                cache.put(classLoader, result);
                return result;
           } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories 
from location [META-INF/spring.factories]", var13);
           }
       }
   }
    private static <T> T instantiateFactory(String instanceClassName, Class<T>
factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, 
classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName
+ "] is not assignable to [" + factoryClass.getName() + "]");
           } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new
Class[0]).newInstance();
           }
       } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory 
class: " + factoryClass.getName(), var4);
       }
   }
}
每个jar都可以定义自己的META-INF/spring.factories ,jar被加载的同时 spring.factories里面定义的
bean就可以自动被加载

Eureka服务注册核心源码解析

EnableEurekaServer注解作用

通过 @EnableEurekaServer 激活EurekaServer
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
此类有一个重要作用:导入EurekaServerMarkerConfifiguration配置类实例化了一个Marker的bean对象,此对象是实例化核心配置类的前提条件
@Configuration
public class EurekaServerMarkerConfiguration {
    public EurekaServerMarkerConfiguration() {
   }
    @Bean
    public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
        return new EurekaServerMarkerConfiguration.Marker();
   }
    class Marker {
        Marker() {
       }
   }
}

自动装载核心配置类

SpringCloud对EurekaServer的封装使得发布一个EurekaServer无比简单,根据自动装载原则可以在spring-cloud-netflix-eureka-server-2.1.0.RELEASE.jar 下找到 spring.factories
EurekaServerAutoConfiguration 是Eureka服务端的自动配置类 
@Configuration
@Import({EurekaServerInitializerConfiguration.class})
@ConditionalOnBean({Marker.class})
@EnableConfigurationProperties({EurekaDashboardProperties.class, 
InstanceRegistryProperties.class})
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
    //...代码省略
}
现在我们展开来说这个Eureka服务端的自动配置类;
  1. 这个配置类实例化的前提条件是上下文中存在 EurekaServerMarkerConfifiguration.Marker 这个bean,解释了上面的问题 
  2. 通过@EnableConfifigurationProperties({ EurekaDashboardProperties.class,InstanceRegistryProperties.class })导入了两个配置类
    1. EurekaDashboardProperties : 配置 EurekaServer的管控台
    2. InstanceRegistryProperties : 配置期望续约数量和默认的通信数量
  3. 通过@Import({EurekaServerInitializerConfifiguration.class})引入启动配置类
EurekaServerInitializerConfifiguration 
@Configuration
public class EurekaServerInitializerConfiguration implements
ServletContextAware, SmartLifecycle, Ordered {
    public void start() {
       (new Thread(new Runnable() {
            public void run() {
                try {
                   
EurekaServerInitializerConfiguration.this.eurekaServerBootstrap.contextInitiali
zed(EurekaServerInitializerConfiguration.this.servletContext);
                    EurekaServerInitializerConfiguration.log.info("Started 
Eureka Server");
                    EurekaServerInitializerConfiguration.this.publish(new
EurekaRegistryAvailableEvent(EurekaServerInitializerConfiguration.this.getEureka
ServerConfig()));
                    EurekaServerInitializerConfiguration.this.running = true;
                    EurekaServerInitializerConfiguration.this.publish(new
EurekaServerStartedEvent(EurekaServerInitializerConfiguration.this.getEurekaServ
erConfig()));
               } catch (Exception var2) {
                    EurekaServerInitializerConfiguration.log.error("Could not 
initialize Eureka servlet context", var2);
               }
           }
       })).start();
   }
可以看到EurekaServerInitializerConfifiguration实现了SmartLifecycle,也就意味着Spring容器启动时会去执行start()方法。加载所有的EurekaServer的配置 

EurekaServerAutoConfifiguration

实例化了EurekaServer的管控台的Controller类 EurekaController
 @Bean
    @ConditionalOnProperty(
        prefix = "eureka.dashboard",
        name = {"enabled"},
        matchIfMissing = true
   )
    public EurekaController eurekaController() {
        return new EurekaController(this.applicationInfoManager);
   }
实例化EurekaServerBootstrap类 
 @Bean
    public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry
registry, EurekaServerContext serverContext) {
        return new EurekaServerBootstrap(this.applicationInfoManager, 
this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext);
   }

实例化jersey相关配置类 

@Bean
    public FilterRegistrationBean jerseyFilterRegistration(Application
eurekaJerseyApp) {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new ServletContainer(eurekaJerseyApp));
        bean.setOrder(2147483647);
        bean.setUrlPatterns(Collections.singletonList("/eureka/*"));
        return bean;
   }
    @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;
   }
jerseyApplication 方法,在容器中存放了一个jerseyApplication对象,jerseyApplication()方法里的东西和Spring源码里扫描@Component逻辑类似,扫描@Path和@Provider标签,然后封装成beandefifinition,封装到Application的set容器里。通过fifilter过滤器来过滤url进行映射到对象的Controller 

暴露的服务端接口

由于集成了Jersey,我们可以找到在EurekaServer的依赖包中的 eureka-core-1.9.8.jar ,可以看到一些列的XXXResource 
这些类都是通过Jersey发布的供客户端调用的服务接口。 

服务端接受客户端的注册

在ApplicationResource.addInstance()方法中可以看到 this.registry.register(info,"true".equals(isReplication)); 
public void register(InstanceInfo info, boolean isReplication) {
         //默认有效时长90m
        int leaseDuration = 90;
        if (info.getLeaseInfo() != null &&
info.getLeaseInfo().getDurationInSecs() > 0) {
            leaseDuration = info.getLeaseInfo().getDurationInSecs();
       }
 //注册实例
        super.register(info, leaseDuration, isReplication);
        //同步到其他EurekaServer服务
        this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, 
info.getAppName(), info.getId(), info, (InstanceStatus)null, isReplication);
   }
继续找到父类的register方法可以看到整个注册的过程 
//线程安全的map,存放所有注册的示例对象
 private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
registry = new ConcurrentHashMap();    
 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);
            //如果第一个实例注册会给registryput进去一个空的
            if (gMap == null) {
                ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new
ConcurrentHashMap();
                gMap = (Map)this.registry.putIfAbsent(registrant.getAppName(), 
gNewMap);
                if (gMap == null) {
                    gMap = gNewMap;
               }
           }
 //根据注册的示例对象id,获取已存在的Lease
            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 {
                Object var6 = this.lock;
                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());
           }
 //将lease存入gMap
           ((Map)gMap).put(registrant.getId(), lease);
            AbstractInstanceRegistry.CircularQueue var20 =
this.recentRegisteredQueue;
            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();
       }
   }

(2)服务端接受客户端的续约

在InstanceResource的renewLease方法中完成客户端的心跳(续约)处理,其中最关键的方法就是this.registry.renew(this.app.getName(), this.id, isFromReplicaNode) 
public boolean renew(String appName, String id, boolean isReplication) {
        //客户端续约
        if (super.renew(appName, id, isReplication)) { 
            //同步到其他的EurekaServer服务
           
this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Heartbeat, appName, 
id, (InstanceInfo)null, (InstanceStatus)null, isReplication);
            return true;
       } else {
            return false;
       }
   }
继续找到父类的renew方法可以看到整个续约的过程 
public boolean renew(String appName, String id, boolean isReplication) {
        EurekaMonitors.RENEW.increment(isReplication);
        Map<String, Lease<InstanceInfo>> gMap = (Map)this.registry.get(appName);
        //从内存map中根据id获取示例对象的Lease对象
        Lease<InstanceInfo> leaseToRenew = null;
        if (gMap != null) {
            leaseToRenew = (Lease)gMap.get(id);
       }
        if (leaseToRenew == null) {
            EurekaMonitors.RENEW_NOT_FOUND.increment(isReplication);
            logger.warn("DS: Registry: lease doesn't exist, registering 
resource: {} - {}", appName, id);
            return false;
       } else {
            //获取示例对象
            InstanceInfo instanceInfo = (InstanceInfo)leaseToRenew.getHolder();
            if (instanceInfo != null) {
                InstanceStatus overriddenInstanceStatus =
this.getOverriddenInstanceStatus(instanceInfo, leaseToRenew, isReplication);
                if (overriddenInstanceStatus == InstanceStatus.UNKNOWN) {
                    logger.info("Instance status UNKNOWN possibly due to deleted 
override for instance {}; re-register required", instanceInfo.getId());
                    EurekaMonitors.RENEW_NOT_FOUND.increment(isReplication);
                    return false;
               }
                if (!instanceInfo.getStatus().equals(overriddenInstanceStatus)) 
{
                    logger.info("The instance status {} is different from 
overridden instance status {} for instance {}. Hence setting the status to 
overridden status", new Object[]{instanceInfo.getStatus().name(), 
instanceInfo.getOverriddenStatus().name(), instanceInfo.getId()});
                    //设置示例状态
                   
instanceInfo.setStatusWithoutDirty(overriddenInstanceStatus);
               }
}
 //设置续约次数
            this.renewsLastMin.increment();
            leaseToRenew.renew();
            return true;
       }
   }

服务剔除

在AbstractInstanceRegistry.postInit()方法,在此方法里开启了一个每60秒调用一次EvictionTask.evict()的定时器。
public void evict(long additionalLeaseMs) {
        logger.debug("Running the evict task");
        if (!this.isLeaseExpirationEnabled()) {
            logger.debug("DS: lease expiration is currently disabled.");
       } else {
            List<Lease<InstanceInfo>> expiredLeases = new ArrayList();
            Iterator var4 = this.registry.entrySet().iterator();
            while(true) {
                Map leaseMap;
                do {
                    if (!var4.hasNext()) {
                        int registrySize = (int)this.getLocalRegistrySize();
                        int registrySizeThreshold = (int)((double)registrySize *
this.serverConfig.getRenewalPercentThreshold());
                        int evictionLimit = registrySize -
registrySizeThreshold;
                        int toEvict = Math.min(expiredLeases.size(), 
evictionLimit);
                        if (toEvict > 0) {
                            logger.info("Evicting {} items (expired={}, 
evictionLimit={})", new Object[]{toEvict, expiredLeases.size(), evictionLimit});
                            Random random = new
Random(System.currentTimeMillis());
                            for(int i = 0; i < toEvict; ++i) {
                                int next = i +
random.nextInt(expiredLeases.size() - i);
                                Collections.swap(expiredLeases, i, next);
                                Lease<InstanceInfo> lease = (Lease)expiredLeases.get(i);
                                String appName =
((InstanceInfo)lease.getHolder()).getAppName();
                                String id =
((InstanceInfo)lease.getHolder()).getId();
                                EurekaMonitors.EXPIRED.increment();
                                logger.warn("DS: Registry: expired lease for 
{}/{}", appName, id);
                                this.internalCancel(appName, id, false);
                           }
                       }
                        return;
                   }
Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry = (Entry)var4.next();
                    leaseMap = (Map)groupEntry.getValue();
               } while(leaseMap == null);
                Iterator var7 = leaseMap.entrySet().iterator();
                while(var7.hasNext()) {
                    Entry<String, Lease<InstanceInfo>> leaseEntry = (Entry)var7.next();
                    Lease<InstanceInfo> lease = (Lease)leaseEntry.getValue();
                    if (lease.isExpired(additionalLeaseMs) && lease.getHolder() 
!= null) {
                        expiredLeases.add(lease);
                   }
               }
           }
       }
   }

 Eureka服务发现核心源码解析 

自动装载

在服务消费者导入的坐标中有 spring-cloud-netflix-eureka-client-2.1.0.RELEASE.jar 找到其中的 spring.factories 可以看到所有自动装载的配置类

服务注册

boolean register() throws Throwable {
        logger.info("DiscoveryClient_{}: registering service...", 
this.appPathIdentifier);
        EurekaHttpResponse httpResponse;
        try {
            httpResponse =
this.eurekaTransport.registrationClient.register(this.instanceInfo);
       } catch (Exception var3) {
            logger.warn("DiscoveryClient_{} - registration failed {}", new
Object[]{this.appPathIdentifier, var3.getMessage(), var3});
            throw var3;
       }
        if (logger.isInfoEnabled()) {
            logger.info("DiscoveryClient_{} - registration status: {}", 
this.appPathIdentifier, httpResponse.getStatusCode());
       }
        return httpResponse.getStatusCode() ==
Status.NO_CONTENT.getStatusCode();
   }

服务下架

 @PreDestroy
    public synchronized void shutdown() {
        if (this.isShutdown.compareAndSet(false, true)) {
            logger.info("Shutting down DiscoveryClient ...");
            if (this.statusChangeListener != null && this.applicationInfoManager
!= null) {
               
this.applicationInfoManager.unregisterStatusChangeListener(this.statusChangeLis
tener.getId());
           }
            this.cancelScheduledTasks();
            if (this.applicationInfoManager != null &&
this.clientConfig.shouldRegisterWithEureka() &&
this.clientConfig.shouldUnregisterOnShutdown()) {
               
this.applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
                this.unregister();
           }
            if (this.eurekaTransport != null) {
                this.eurekaTransport.shutdown();
           }
            this.heartbeatStalenessMonitor.shutdown();
            this.registryStalenessMonitor.shutdown();
            logger.info("Completed shut down of DiscoveryClient");
       }
   }

心跳续约

在com.netflflix.discovery.DiscoveryClient.HeartbeatThread中定义了续约的操作,我们查看renew()方法;
boolean renew() {
        try {
            EurekaHttpResponse<InstanceInfo> httpResponse =
this.eurekaTransport.registrationClient.sendHeartBeat(this.instanceInfo.getAppNa
me(), this.instanceInfo.getId(), this.instanceInfo, (InstanceStatus)null);
            logger.debug("DiscoveryClient_{} - Heartbeat status: {}", 
this.appPathIdentifier, httpResponse.getStatusCode());
            if (httpResponse.getStatusCode() ==
Status.NOT_FOUND.getStatusCode()) {
                this.REREGISTER_COUNTER.increment();
                logger.info("DiscoveryClient_{} - Re-registering apps/{}", 
this.appPathIdentifier, this.instanceInfo.getAppName());
                long timestamp = this.instanceInfo.setIsDirtyWithTime();
                boolean success = this.register();
                if (success) {
                    this.instanceInfo.unsetIsDirty(timestamp);
               }
                return success;
           } else {
                return httpResponse.getStatusCode() ==
Status.OK.getStatusCode();
           }
       } catch (Throwable var5) {
            logger.error("DiscoveryClient_{} - was unable to send heartbeat!", 
this.appPathIdentifier, var5);
            return false;
       }
   }
在renew()这个方法中,首先向注册中心执行了心跳续约的请求,StatusCode为200成功,若为404则执行register()重新注册操作;
最后总结一下eureka客户端做的事情:
  1.根据配置文件初始化bean,创建客户端实例信息 InstanceInfo
  2.第一次全量拉取注册中心服务列表(url=/apps),初始化周期任务:
    2.1 CacheRefreshThread 定时刷新本地缓存服务列表,若是客户端第一次拉取,则会全量拉取,后面则增量拉取.若增量拉取失败则全量拉取,配置属性为eureka.client.registryFetchIntervalSeconds=30默认拉取一次;
    2.2 HeartbeatThread 通过renew()续约任务,维持于注册中心的心跳(url=/apps/{id}),若返回状态码为404则说明该服务实例没有在注册中心注册,执行register()向注册中心注册实例信息;
    2.3 ApplicationInfoManager.StatusChangeListener 注册实例状态监听类,监听服务实例状态变化,向注册中心同步实例状态;
    2.4 InstanceInfoReplicator 定时刷新实例状态,并向注册中心同步,默认eureka.client.instanceInfoReplicationIntervalSeconds=30执行一次.若实例状态有变更,则重新执行注册; 
posted @ 2021-07-27 12:57  1640808365  阅读(212)  评论(0编辑  收藏  举报