2023-08-07  阅读(4)
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/239

在Eureka注册中心的启动过程中,创建了一个ApplicationInfoManager对象,这是Eureka中的一个应用信息管理组件:

    applicationInfoManager = new ApplicationInfoManager(
                        instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());

202308072151226341.png

本章,我们就来看看ApplicationInfoManager和InstanceInfo的底层实现。

一、InstanceInfo

InstanceInfo代表了Eureka应用实例信息。Eureka-Client 向 Eureka-Server 注册 该对象信息,注册成功后,可以被其它 Eureka-Client 发现

1.1 Builder模式

InstanceInfo包含了非常多的参数,它的构造依赖EurekaInstanceConfig。所以,Eureka采用了设计模式里的Builder模式进行InstanceInfo对象的构造,Builder模式非常适合这种复杂对象的构造。

具体的构造由EurekaConfigBasedInstanceInfoProvider来完成:

    new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get()

我们来看下它的get方法:

    private final EurekaInstanceConfig config;
    private InstanceInfo instanceInfo;
    
    public synchronized InstanceInfo get() {
        if (instanceInfo == null) {
            // 创建租约信息构建器,并设置属性
            LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
                .setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
                .setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
    
            // 创建VIP地址解析器
            if (vipAddressResolver == null) {
                vipAddressResolver = new Archaius1VipAddressResolver();
            }
    
            // 创建应用实例Buidler
            InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(vipAddressResolver);
    
            // 设置应用实例编号
            String instanceId = config.getInstanceId();
            DataCenterInfo dataCenterInfo = config.getDataCenterInfo();
            if (instanceId == null || instanceId.isEmpty()) {
                if (dataCenterInfo instanceof UniqueIdentifier) {
                    instanceId = ((UniqueIdentifier) dataCenterInfo).getId();
                } else {
                    instanceId = config.getHostName(false);
                }
            }
    
            // 获得主机名
            String defaultAddress;
            if (config instanceof RefreshableInstanceConfig) {
                // Refresh AWS data center info, and return up to date address
                defaultAddress = ((RefreshableInstanceConfig) config).resolveDefaultAddress(false);
            } else {
                defaultAddress = config.getHostName(false);
            }
            // fail safe
            if (defaultAddress == null || defaultAddress.isEmpty()) {
                defaultAddress = config.getIpAddress();
            }
    
            // 设置应用实例的属性
            builder.setNamespace(config.getNamespace())
                .setInstanceId(instanceId)
                .setAppName(config.getAppname())
                .setAppGroupName(config.getAppGroupName())
                .setDataCenterInfo(config.getDataCenterInfo())
                .setIPAddr(config.getIpAddress())
                .setHostName(defaultAddress) // 主机名
                .setPort(config.getNonSecurePort())
                .enablePort(PortType.UNSECURE, config.isNonSecurePortEnabled())
                .setSecurePort(config.getSecurePort())
                .enablePort(PortType.SECURE, config.getSecurePortEnabled())
                .setVIPAddress(config.getVirtualHostName()) // VIP 地址
                .setSecureVIPAddress(config.getSecureVirtualHostName())
                .setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
                .setStatusPageUrl(config.getStatusPageUrlPath(), config.getStatusPageUrl())
                .setASGName(config.getASGName())
                .setHealthCheckUrls(config.getHealthCheckUrlPath(),
                                    config.getHealthCheckUrl(), config.getSecureHealthCheckUrl());
    
            // 应用初始化后是否开启:不开启-应用实例处于STARTING状态;开启-应用实例处于UP状态
            if (!config.isInstanceEnabledOnit()) {
                InstanceStatus initialStatus = InstanceStatus.STARTING;
                LOG.info("Setting initial instance status as: " + initialStatus);
                builder.setStatus(initialStatus);
            } else {
                LOG.info("Setting initial instance status as: {}. This may be too early for the instance to advertise "
                         + "itself as available. You would instead want to control this via a healthcheck handler.",
                         InstanceStatus.UP);
            }
    
            // 设置 应用实例构建器 的 元数据( Metadata )集合
            for (Map.Entry<String, String> mapEntry : config.getMetadataMap().entrySet()) {
                String key = mapEntry.getKey();
                String value = mapEntry.getValue();
                builder.add(key, value);
            }
    
            // 基于Builder模式,创建应用实例对象
            instanceInfo = builder.build();
    
            // 设置应用实例的租约信息
            instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
        }
        return instanceInfo;
    }

可以看到,InstanceInfo应用实例对象的创建是基于InstanceInfo.Builder这个Builder构建器:

    public static final class Builder {
        //...
        private InstanceInfo result;
        private final VipAddressResolver vipAddressResolver;
    
        private Builder(InstanceInfo result, VipAddressResolver vipAddressResolver) {
            this.vipAddressResolver = vipAddressResolver;
            this.result = result;
        }
    
        public static Builder newBuilder() {
            return new Builder(new InstanceInfo(), LazyHolder.DEFAULT_VIP_ADDRESS_RESOLVER);
        }
    
        public static Builder newBuilder(VipAddressResolver vipAddressResolver) {
            return new Builder(new InstanceInfo(), vipAddressResolver);
        }
        //...
    }

最终的InstanceInfo应用实例的构造完成是在调用InstanceInfo.Builder.build()方法时:

    public InstanceInfo build() {
        if (!isInitialized()) {
            throw new IllegalStateException("name is required!");
        }
        return result;
    }

builder对象针对InstanceInfo实例的各种set方法进行了一些处理,去set各种需要的属性和配置,设置完成后就完成了最终的一个复杂InstanceInfo应用实例对象的构造。比如:

    public Builder setNamespace(String namespace) {
        this.namespace = namespace.endsWith(".")
            ? namespace
            : namespace + ".";
        return this;
    }

二、ApplicationInfoManager

应用实例InstanceInfo构建完成后,会保存到ApplicationInfoManager中。ApplicationInfoManager,顾名思义,就是一个应用信息管理器。

因为InstanceInfo就是一个单纯的POJO,不带任何业务行为,所以需要一个外部组件对它进行管理,所以就有了ApplicationInfoManager:

    public class ApplicationInfoManager {
    
        // 单例
        private static ApplicationInfoManager instance = new ApplicationInfoManager(null, null, null);
    
        // 状态变更监听器集合
        protected final Map<String, StatusChangeListener> listeners;
    
        // 应用实例状态匹配
        private final InstanceStatusMapper instanceStatusMapper;
    
        // 应用实例信息
        private InstanceInfo instanceInfo;
    
        // 应用实例配置
        private EurekaInstanceConfig config;
    
        //...
    
        public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo, OptionalArgs optionalArgs) {
            this.config = config;
            this.instanceInfo = instanceInfo;
            this.listeners = new ConcurrentHashMap<String, StatusChangeListener>();
            if (optionalArgs != null) {
                this.instanceStatusMapper = optionalArgs.getInstanceStatusMapper();
            } else {
                this.instanceStatusMapper = NO_OP_MAPPER;
            }
    
            // Hack to allow for getInstance() to use the DI'd ApplicationInfoManager
            instance = this;
        }
    
        //...
    }

可以看到,ApplicationInfoManager提供了很多管理InstanceInfo的方法:

202308072151233712.png

三、总结

本章,我介绍了InstanceInfo应用实例,它的特点就是利用了Builder设计模式 进行对象的构造。对于InstanceInfo对象的管理,Eureka则采用了一个ApplicationInfoManager对象。对于InstanceInfo应用实例信息,后面在构造EurekaClient对象时还会用到。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文