专栏首页BAT的乌托邦[享学Eureka] 五、Eureka核心概念:应用(Application)和注册表(Applications)

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

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

前言

通过前面文章我们已经了解了Eureka的核心概念之一:实例InstanceInfo,实例可以说是Eureka操作的最小单位。本文继续介绍其两个范围更广的概念:应用(Application)和注册表(Applications)。


正文

如果把实例类比于Java中的对象,那么应用Application就好比Class类,很明显Eureka管理着非常非常多的“类”,这便是它的注册表Applications


Application 应用

它代表一个应用:持有一堆的InstanceInfo实例。如Account应用,会对应着一对的account实例。


成员属性

@Serializer("com.netflix.discovery.converters.EntityBodyConverter")
// 注意它的序列化/反序列化 {"application": {...}}
@XStreamAlias("application")
@JsonRootName("application")
public class Application {
	
	private static Random shuffleRandom = new Random();
	
    private String name;
    @XStreamOmitField
    private volatile boolean isDirty = false;
    @XStreamImplicit
    private final Set<InstanceInfo> instances;
    // 存储被打乱了的实例列表
    private final AtomicReference<List<InstanceInfo>> shuffledInstances;
    // 缓存实例id和InstanceInfo的对应关系
    private final Map<String, InstanceInfo> instancesMap;
	
	
	// 反序列化时通过此构造器来生成一个Application应用
    @JsonCreator
    public Application(@JsonProperty("name") String name,
            @JsonProperty("instance") List<InstanceInfo> instances) {
        this(name);
        for (InstanceInfo instanceInfo : instances) {
            addInstance(instanceInfo);
        }
    }

	// 关于name的设置提供了方法(带有缓存的)
    public void setName(String name) {
        this.name = StringCache.intern(name);
    }
}
  • Random shuffleRandom :Collections.shuffle()时传入,用于随机打乱infoList的顺序
  • name:应用名称 如:account
  • isDirty:标记该应用是否已经“脏”了。但凡改变了实例的个数(新增/减少)时就标记其脏了
  • instances:存储实例们的Set集合(自带去重,实例id相同代表同一实例)。默认是LinkedHashSet类型,具有顺序的
    • 该集合只能通过两个方法addInstance/removeInstance来改变其值
  • shuffledInstances:乱序后的实例集合。
  • instancesMap:缓存ConcurrentHashMap类型。key是实例id,value是对应的实例对象

成员方法

Application:

	// 添加一个实例(放在最上面。这是先remove后add的目的)
    public void addInstance(InstanceInfo i) {
        instancesMap.put(i.getId(), i);
        synchronized (instances) {
            instances.remove(i);
            instances.add(i);
            isDirty = true;
        }
    }
    // 移除一个  并且标注isDirty = true;
    public void removeInstance(InstanceInfo i) {
        removeInstance(i, true);
    }
    private void removeInstance(InstanceInfo i, boolean markAsDirty) {
        instancesMap.remove(i.getId());
        synchronized (instances) {
            instances.remove(i);
            if (markAsDirty) {
                isDirty = true;
            }
        }
    }

    @JsonIgnore
    public List<InstanceInfo> getInstancesAsIsFromEureka() {
        synchronized (instances) {
           return new ArrayList<InstanceInfo>(this.instances);
        }
    }
	// 返回所有的实例列表  注意:这里返回的是shuffledInstances乱序的Infos
	// 若乱序的为null,就返回正常顺序的:getInstancesAsIsFromEureka
    @JsonProperty("instance")
    public List<InstanceInfo> getInstances() {
        return Optional.ofNullable(shuffledInstances.get()).orElseGet(this::getInstancesAsIsFromEureka);
    }

	
	// 根据id拿到一个指定的Instance实例
    public InstanceInfo getByInstanceId(String id) {
        return instancesMap.get(id);
    }
    public int size() {
        return instances.size();
    }


	// 将应用程序中的实例列表打乱并将其存储用来检索
	// filterUpInstances:是否要过滤出来剩下InstanceStatus.UP的实例
	// indexByRemoteRegions:
    public void shuffleAndStoreInstances(boolean filterUpInstances) {
        _shuffleAndStoreInstances(filterUpInstances, false, null, null, null);
    }
    public void shuffleAndStoreInstances(Map<String, Applications> remoteRegionsRegistry,
                                         EurekaClientConfig clientConfig, InstanceRegionChecker instanceRegionChecker) {
        _shuffleAndStoreInstances(clientConfig.shouldFilterOnlyUpInstances(), true, remoteRegionsRegistry, clientConfig, instanceRegionChecker);
    }

这里唯一有疑问的是:为何要shuffle打乱呢?其实这和Eureka踢出实例是有关的,后文再专文分享。


Applications 注册表

该类用于封装由Eureka Server返回的所有注册表信息的类。


成员属性

@Serializer("com.netflix.discovery.converters.EntityBodyConverter")
@XStreamAlias("applications")
@JsonRootName("applications")
public class Applications {

    private static final String STATUS_DELIMITER = "_";

    private String appsHashCode;
    private Long versionDelta;
    @XStreamImplicit
    private final AbstractQueue<Application> applications;
    private final Map<String, Application> appNameApplicationMap;
    private final Map<String, VipIndexSupport> virtualHostNameAppMap;
    private final Map<String, VipIndexSupport> secureVirtualHostNameAppMap;


	// registeredApplications:已经注册的应用们
    @JsonCreator
    public Applications(@JsonProperty("appsHashCode") String appsHashCode,
            @JsonProperty("versionDelta") Long versionDelta,
            @JsonProperty("application") List<Application> registeredApplications) {
        this.applications = new ConcurrentLinkedQueue<Application>();
        this.appNameApplicationMap = new ConcurrentHashMap<String, Application>();
        this.virtualHostNameAppMap = new ConcurrentHashMap<String, VipIndexSupport>();
        this.secureVirtualHostNameAppMap = new ConcurrentHashMap<String, VipIndexSupport>();
        this.appsHashCode = appsHashCode;
        this.versionDelta = versionDelta;

		// 每个Application应用都添加进Map or Queue里面去存储着
        for (Application app : registeredApplications) {
            this.addApplication(app);
        }
    }
}
  • STATUS_DELIMITER:作为getReconcileHashCode的分隔符
  • appsHashCode:由eureka服务器使用。不可外用。为应用集合分配的HashCode散列值。该值一般来自于getReconcileHashCode()方法
  • versionDelta:已过期属性,不用搭理
  • applications:包含的所有Application应用们。使用AbstractQueue装载,实际是个ConcurrentLinkedQueue队列(特点:FIFO)
  • appNameApplicationMap:map缓存。key是应用名,value是应用实例本身。
  • virtualHostNameAppMap:map缓存。key是InstanceInfo.vipAddress,value是VipIndexSupport(持有多个InstanceInfo实例)
    • 说明:vipAddress为null是不会放进Map里去的
  • secureVirtualHostNameAppMap:同上。区别是它用的InstanceInfo#secureVipAddress做key
    • 说明:vipAddress为null是不会放进Map里去的

成员方法

Applications:

	// 添加一个应用
    public void addApplication(Application app) {
        appNameApplicationMap.put(app.getName().toUpperCase(Locale.ROOT), app);
        addInstancesToVIPMaps(app, this.virtualHostNameAppMap, this.secureVirtualHostNameAppMap);
        applications.add(app);
    }
    // 移除一个Application应用
    public void removeApplication(Application app) {
        this.appNameApplicationMap.remove(app.getName().toUpperCase(Locale.ROOT));
        this.applications.remove(app);
    }

	// 得到已经注册的Applications应用们
    @JsonProperty("application")
    public List<Application> getRegisteredApplications() {
        return new ArrayList<>(this.applications);
    }
    // 根据应用名获取应用
    public Application getRegisteredApplications(String appName) {
        return appNameApplicationMap.get(appName.toUpperCase(Locale.ROOT));
    }

	// 注意:这是获取所有应用的instances实例总和,并不是应用综合哦~~~~~
    public int size() {
        return applications.stream().mapToInt(Application::size).sum();
    }

	// 获取此**应用程序实例们**的哈希代码。用于比较eureka服务器和eureka客户端之间的实例。
	// 一致性hash   Reconcile:一致性
	// hash值会通过STATUS_DELIMITER来拼接~~~~~~
    @JsonIgnore
    public String getReconcileHashCode() {
        TreeMap<String, AtomicInteger> instanceCountMap = new TreeMap<String, AtomicInteger>();
        populateInstanceCountMap(instanceCountMap);
        return getReconcileHashCode(instanceCountMap);
    }

	// 打乱InstanceInfo实例们
	// 针对每个Application都会打乱,内部依赖于Application#shuffleAndStoreInstances实现的
    public void shuffleInstances(boolean filterUpInstances) {
        shuffleInstances(filterUpInstances, false, null, null, null);
    }
    public void shuffleAndIndexInstances(Map<String, Applications> remoteRegionsRegistry,
            							EurekaClientConfig clientConfig, InstanceRegionChecker instanceRegionChecker) {
        shuffleInstances(clientConfig.shouldFilterOnlyUpInstances(), true, remoteRegionsRegistry, clientConfig, instanceRegionChecker);
    }

总之,Applications包装Eureka服务器返回的所有注册表信息的类。

需要注意的是:注册表信息是从EurekaClientConfig.getRegistryFetchIntervalSeconds()中指定的eureka Server取的(也就是这些Application们)。获取信息后,将对其进行洗牌打乱,并对配置EurekaClientConfig.shouldFilterOnlyUpInstances()指定的InstanceInfo.InstanceStatus.UP状态的实例进行筛选。

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

我来说两句

0 条评论
登录 后参与评论

相关文章

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

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

    YourBatman
  • 【小家Spring】Spring MVC容器的web九大组件之---HandlerMapping源码详解(一)---BeanNameUrlHandlerMapping系列

    在这篇文章里: 【小家Spring】Spring MVC容器启动时,web九大组件初始化详解(Spring MVC的运行机制) 已经大概介绍过web九大组件...

    YourBatman
  • [享学Netflix] 一、Apache Commons Configuration:你身边的配置管理专家

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

    YourBatman
  • 从一次线上故障来看redis删除机制

    公司去年上线一个抽奖系统,主要用来拉新、提升流量,所有新注册的用户在指定时间都可以抽奖,为了保证安全性,程序中做了频率限制,每个用户30秒只能抽1次,...

    心平气和
  • 建造者模式(Builder)及其应用

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

    用户2965908
  • 咦,拆分个字符串都这么讲究

    提到拆分字符串,我猜你十有八九会撂下一句狠话,“这有什么难的,直接上 String 类的 split() 方法不就拉到了!”假如你真的这么觉得,那可要注意了,事...

    沉默王二
  • 使用@Value 和@PropertySource 实现为属性赋值

    基于 SpEL 表达式也很简单,只需要写 SpEL 表达式,可以参考 Spring Expression Language(SpEL)速查指南

    水货程序员
  • 零基础学编程035:群发邮件并不难

    我是GTD的重度用户,GTD中讲究将所有事情先收集起来再说,所以收集操作越快越好,这样才不至于把手边的工作打断。很多老牌的GTD工具软件支持发邮件实现快速收集,...

    申龙斌
  • HAWQ取代传统数仓实践(六)——增加列

            业务的扩展或变化是不可避免的,尤其像互联网行业,需求变更已经成为常态,唯一不变的就是变化本身,其中最常碰到的扩展是给一个已经存在的表曾加列。  ...

    用户1148526
  • 简单粗暴解读Cortex-M23/33(下)

    上篇文章,我们揭秘了 Cortex-M 家族的新成员、ARMv8-M 架构的两位先驱——传承自 Cortex-M0/M0+ 的 Cortex-M23 和传承自 ...

    GorgonMeducer 傻孩子

扫码关注云+社区

领取腾讯云代金券