专栏首页BAT的乌托邦[享学Eureka] 六、InstanceInfo实例管理器:ApplicationInfoManager

[享学Eureka] 六、InstanceInfo实例管理器:ApplicationInfoManager

代码下载地址:https://github.com/f641385712/netflix-learning

前言

前面文章已经介绍了InstanceInfo,知道了它是个不带行为的近似POJO类,特点是属性特别的多,因此直接使用起来挺笨重的且多有不便。在加上它并没有重要逻辑的管理,因此本文介绍一个它的管理类:ApplicationInfoManager,以后你若要操作InstanceInfo一般均可通过它来完成。

小吐槽一句:该类命名为InstanceInfoManager其实更为合适。因为它管理的是InstanceInfo实例而非应用Application。


正文

ApplicationInfoManager是一个综合类,它有多个静态内部接口/类:

  • StatusChangeListener:实例状态监听器,当实例状态发生改变时会触发
public static interface StatusChangeListener {
    String getId();
    void notify(StatusChangeEvent statusChangeEvent);
}
  • InstanceStatusMapper:实例状态的映射。函数式接口:指定一个map映射策略,在设置新状态的时候经其映射得到最终状态
public static interface InstanceStatusMapper {
    InstanceStatus map(InstanceStatus prev);
}

该状态规则/策略由外部指定,否则即为默认策略(原样输出):

private static final InstanceStatusMapper NO_OP_MAPPER = prev -> prev;
  • OptionalArgs:说白了,它就是对InstanceStatusMapper进行Optional的一个包装
public static class OptionalArgs {
    private InstanceStatusMapper instanceStatusMapper;
    @com.google.inject.Inject(optional = true)
    public void setInstanceStatusMapper(InstanceStatusMapper instanceStatusMapper) {
        this.instanceStatusMapper = instanceStatusMapper;
    }
    InstanceStatusMapper getInstanceStatusMapper() {
        return instanceStatusMapper == null ? NO_OP_MAPPER : instanceStatusMapper;
    }
}

另外此处说句题外话:从代码处可见它一会使用标准的javax.inject.Inject注解,一会用Google Guice自家的Inject注解,这也是Spring Cloud没法直接全盘接过来其内置的DI依赖的原因(SC自己完全实现了一套基于Spring的依赖管理来整合Eureka的)。


ApplicationInfoManager

我们知道InstanceInfo是全局独一份,而实例的状态InstanceStatus是很重要的一个指标,而这个指标就是通过此管理器来管理的。


成员属性

@Singleton
public class ApplicationInfoManager {
	
    private static ApplicationInfoManager instance = new ApplicationInfoManager(null, null, null);
    // please use DI instead
    @Deprecated
    public static ApplicationInfoManager getInstance() {
        return instance;
    }

    protected final Map<String, StatusChangeListener> listeners;
    private final InstanceStatusMapper instanceStatusMapper;
    private InstanceInfo instanceInfo;
    private EurekaInstanceConfig config;


	// 初始化一个ApplicationInfoManager对象
	// OptionalArgs目的就是传递一个`InstanceStatusMapper`实例
    @Inject
    public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo, OptionalArgs optionalArgs) {
        this.config = config;
        this.instanceInfo = instanceInfo;
        this.listeners = new ConcurrentHashMap<>();
        if (optionalArgs != null) {
            this.instanceStatusMapper = optionalArgs.getInstanceStatusMapper();
        } else {
            this.instanceStatusMapper = NO_OP_MAPPER;
        }
        // 把通过DI生成的实例重新赋值给它,已达到统一的效果
        instance = this;
    }

}
  • instance:单例模式嘛。不过已经不推荐使用了,而推荐你使用DI来获得该单例
    • 在Spring Cloud下由EurekaClientAutoConfiguration负责创建单例
  • Map<String, StatusChangeListener> listeners:key是StatusChangeListener的id
    • StatusChangeListener是一个ApplicationInfoManager的静态内部类:用于监听StatusChangeEvent事件
    • StatusChangeEvent事件代表着InstanceInfo实例状态发生了变更,它有且仅有两个地方发送此事件:
      • ApplicationInfoManager#setInstanceStatus:若状态发生了变更便会发送此事件,从而触发StatusChangeListener监听器
      • DiscoveryClient#onRemoteStatusChanged:发送StatusChangeEvent事件。但是,但是,但是触发的监听器是EurekaEventListener
        • 该监听器监听的EurekaEvent事件,而StatusChangeEvent继承自EurekaEvent
        • EurekaEvent有且仅有两个实现类:StatusChangeEventCacheRefreshedEvent(该事件在和Ribbon整合的时候会用到,因为缓存变了,Ribbon的服务列表也得跟着变~)
  • InstanceStatusMapper instanceStatusMapper:一个函数式接口public InstanceStatus map(InstanceStatus prev)用于根据prev之前的状态做个map映射。它唯一被调用的地方是在setInstanceStatus里,具体的map规则由调用者指定
  • InstanceInfo instanceInfo:管理的实例(毕竟实例也是单例嘛~)
  • EurekaInstanceConfig config:管理的实例配置

成员方法

ApplicationInfoManager:

	// 如果你使用getInstance()得到的实例,记得调用它完成初始化
	// 如果是DI方式,就不用啦
    public void initComponent(EurekaInstanceConfig config) {
        try {
            this.config = config;
            this.instanceInfo = new EurekaConfigBasedInstanceInfoProvider(config).get();
        } catch (Throwable e) {
            throw new RuntimeException("Failed to initialize ApplicationInfoManager", e);
        }
    }

	// 获取管理的实例
    public InstanceInfo getInfo() {
        return instanceInfo;
    }
    public EurekaInstanceConfig getEurekaInstanceConfig() {
        return config;
    }


	// 给实例**添加**  最终会进入到InstanceInfo#metadata元数据的Map里
    public void registerAppMetadata(Map<String, String> appMetadata) {
        instanceInfo.registerRuntimeMetadata(appMetadata);
    }


	// =================最重要的一个方法=================
	// 设置此实例的状态。应用程序可以使用它来指示是否准备接收流量。
	// 在这里设置状态也会通知所有已注册的侦听器状态更改事件的。
	// 如果你是自己通过InstanceInfo.setStatus去设置状态,那监听器啥的都不会触发的~~~~~

	// 另外:设进来的状态需要经过instanceStatusMapper策略处理一下(默认策略是不处理)
    public synchronized void setInstanceStatus(InstanceStatus status) {
        InstanceStatus next = instanceStatusMapper.map(status);
        if (next == null) {
            return;
        }

		// 如果prev不为null,那就证明状态发生了变更,就要触发变更的监听器
        InstanceStatus prev = instanceInfo.setStatus(next);
        if (prev != null) {
            for (StatusChangeListener listener : listeners.values()) {
                try {
                    listener.notify(new StatusChangeEvent(prev, next));
                } catch (Exception e) {
                    logger.warn("failed to notify listener: {}", listener.getId(), e);
                }
            }
        }
    }

	// 注册状态变更监听器
	// DiscoveryClient里会通过此方法注册一个监听器来监控实例状态:若状态变更变为Down了就立马同步给Server
    public void registerStatusChangeListener(StatusChangeListener listener) {
        listeners.put(listener.getId(), listener);
    }
    public void unregisterStatusChangeListener(String listenerId) {
        listeners.remove(listenerId);
    }


	// 重新获取主机名以检查它是否已更改  也就是维护实例的setHostName、setIPAddr
	// 说明:只有当你数据中心使用的AmazonInfo 
	// 或者 是RefreshableInstanceConfig动态配置时,才有可能有newAddress的可能,否则不可能变的
	public void refreshDataCenterInfoIfRequired() { ... }
	// 续租信息同步一把,若有需要的话。
	// 这主要考虑到`EurekaInstanceConfig`可能是个可动态刷新的实例,所以通过这个方法去判断一把是否需要同步
	// 这两个方法的调用处是:DiscoveryClient#refreshInstanceInfo
	public void refreshLeaseInfoIfRequired() { ... }

作用总结

ApplicationInfoManager有何用做出总结:

  • 对全局唯一实例InstanceInfo进行管理:
    • 管理的实例一般由构造器传入,亦可由new EurekaConfigBasedInstanceInfoProvider(config).get()获取
    • 通过registerAppMetadata()方法为InstanceInfo的元数据添加k-v数据
    • 提供public方法setInstanceStatus()InstanceInfo设置新的InstanceStatus实例状态
      • InstanceInfo#setStatus自己的设置方法的唯一调用处就是在此,因此请勿直接调用InstanceInfo#setStatus来更改实例的状态,那太偏底层了
      • 通过此方法设置状态,若状态有变化,便会触发StatusChangeListener监听器,从而进行后续一些列的逻辑(如:立马同步状态给Server)
  • 提供注册、取消注册用于监控InstanceInfo实例的监听器StatusChangeListener的方法
  • 提供refreshDataCenterInfoIfRequired()方法用于刷新/同步数据中心信息
  • 提供refreshLeaseInfoIfRequired()方法用于刷新/同步LeaseInfo信息

总结

关于InstanceInfo实例的管理器:ApplicationInfoManager就先介绍到这。最后我想特别说明的是:ApplicationInfoManager也是单例,推荐使用DI来初始化该单例。非Spring环境使用内置的Guice去完成,Spring Cloud下就使用它自己的DI容器完成喽。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [享学Eureka] 五、Eureka核心概念:应用(Application)和注册表(Applications)

    代码下载地址:https://github.com/f641385712/netflix-learning

    YourBatman
  • HandlerMethodArgumentResolver(四):自定参数解析器处理特定应用场景,介绍PropertyNamingStrategy的使用【享学Spring MVC】

    前面通过三篇文章介绍了HandlerMethodArgumentResolver这个参数解析器以及它的所有内置实现,相信看过的小伙伴对它的加载、初始化、处理原理...

    YourBatman
  • 玩转Spring Cache --- 整合分布式缓存Redis Cache(使用Lettuce、使用Spring Data Redis)【享学Spring】

    最近都在聊Spring的缓存抽象Spring Cache,上篇文章深入介绍了Spring Cache集成进程缓存的第三方组件如Caffeine、Ehcache,...

    YourBatman
  • C# 数据操作系列 - 7. EF Core 导航属性配置

    在上一篇,大概介绍了Entity Framework Core关于关系映射的逻辑。在上一篇中留下了EF的外键映射没有说,也就是一对一,一对多,多对一,多对多的关...

    程序员小高
  • 密码发展史之近现代密码

    一、近代密码阶段 近代密码是指从第一次世界大战、第二次世界大战到1976年这段时期密码的发展阶段。 电报的出现第一次使远距离快速传递信息成为可能,事实上,它增强...

    安智客
  • 自助法(bootstrap)在统计检验中的应用及R语言实现过程

    Bootstrap(自助法、自举法)是非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法。

    用户7585161
  • 提高iOS UI开发效率的解决方案QMUI iOS开源

    QMUI 致力于提高各个平台的 UI 开发效率,目前 Web、iOS、Android 三个平台对应的框架均已在 Github 开源。 本文将着重介绍 QMUI...

    腾讯开源
  • VBA解析VBAProject 00

    在每一个带有VBA代码的文件里,都会有一个VBAProject,在VBA编辑器的工程资源窗口可以看到:

    xyj
  • 使用云函数快速打造公众号自动回复机器人

    想法 于上周五发现了一个有趣的仓库: co-wechat(https://github.com/node-webot/co-wechat)。突发奇想,我不要你...

    腾讯云serverless团队
  • SQL Server 2012安装错误:Error while enabling Windows feature: NetFx3, Error Code: -2146498298

    最近在安装SQL server 2012时,碰到错误:Error while enabling Windows feature: NetFx3, Error C...

    Leshami

扫码关注云+社区

领取腾讯云代金券