Eureka Client 源码解析-更新本地配置到Server

Eureka Client 源码解析这篇文章中,说到按需注册定时任务的时候,当时没有对 InstanceInfoReplicator#run 方法中的DiscoveryClient#refreshInstanceInfo 方法中的一些调用进行解析,因此这里来就这个来进行一下简单的解析。

首先在 DiscoveryClient#refreshInstanceInfo 方法中开始有两个方法的调用ApplicationInfoManager#refreshDataCenterInfoIfRequired 和 ApplicationInfoManager#refreshLeaseInfoIfRequired,本篇文章就将会就这两个方法的调用进行一些解析。

refreshDataCenterInfoIfRequired 方法

从方法命名上,可以看出这个方法是用来刷新数据中心信息,这个方法主要是获取主机名,并检查它是否有改变,如果有的话,就对整个DataCenterInfo 进行重新获取,并传给 server 端,用于下一次的心跳。但是这里面主要是用于亚马逊的数据中心,因此一般情况是用不到的,所以这里就做一个简单的解析。那么这里来看看这个方法的代码实现,如下:

/**
 * Refetches the hostname to check if it has changed. If it has, the entire
 * <code>DataCenterInfo</code> is refetched and passed on to the eureka
 * server on next heartbeat.
 *
 * see {@link InstanceInfo#getHostName()} for explanation on why the hostname is used as the default address
 */
public void refreshDataCenterInfoIfRequired() {
    // 获取主机名
    String existingAddress = instanceInfo.getHostName();

    String existingSpotInstanceAction = null;
    // 判断是否属于亚马逊数据中心
    if (instanceInfo.getDataCenterInfo() instanceof AmazonInfo) {
        existingSpotInstanceAction = ((AmazonInfo) instanceInfo.getDataCenterInfo()).get(AmazonInfo.MetaDataKey.spotInstanceAction);
    }

    String newAddress;
    if (config instanceof RefreshableInstanceConfig) {
        // Refresh data center info, and return up to date address
        newAddress = ((RefreshableInstanceConfig) config).resolveDefaultAddress(true);
    } else {
        newAddress = config.getHostName(true);
    }
    String newIp = config.getIpAddress();

    if (newAddress != null && !newAddress.equals(existingAddress)) {
        logger.warn("The address changed from : {} => {}", existingAddress, newAddress);
        updateInstanceInfo(newAddress, newIp);
    }
    // 判断配置文件中的数据中心是否属于 AmazonInfo
    if (config.getDataCenterInfo() instanceof AmazonInfo) {
        String newSpotInstanceAction = ((AmazonInfo) config.getDataCenterInfo()).get(AmazonInfo.MetaDataKey.spotInstanceAction);
        if (newSpotInstanceAction != null && !newSpotInstanceAction.equals(existingSpotInstanceAction)) {
            logger.info(String.format("The spot instance termination action changed from: %s => %s",
                    existingSpotInstanceAction,
                    newSpotInstanceAction));
            // 更新实例信息
            updateInstanceInfo(null , null );
        }
    }        
}

refreshLeaseInfoIfRequired 方法

这个方法是用与刷新租约信息的,这里先看下这个方法的实现,代码如下:

public void refreshLeaseInfoIfRequired() {
    LeaseInfo leaseInfo = instanceInfo.getLeaseInfo();
    if (leaseInfo == null) {
        return;
    }
    // 从配置文件中获取租约过期期间为多少
    int currentLeaseDuration = config.getLeaseExpirationDurationInSeconds();
    // 获取租约续约的时间间隔
    int currentLeaseRenewal = config.getLeaseRenewalIntervalInSeconds();
    // 判断是否满足条件
    if (leaseInfo.getDurationInSecs() != currentLeaseDuration || leaseInfo.getRenewalIntervalInSecs() != currentLeaseRenewal) {
        // 这是迭代稳定性的变化使用
        LeaseInfo newLeaseInfo = LeaseInfo.Builder.newBuilder()
                .setRenewalIntervalInSecs(currentLeaseRenewal)
                .setDurationInSecs(currentLeaseDuration)
                .build();
        instanceInfo.setLeaseInfo(newLeaseInfo);
        // 设置dirty
        instanceInfo.setIsDirty();
    }
}

迭代稳定性:一般情况下,对于多线程操作的共享数组、集合,我们对其元素进行修改操作时,不要直接对该数组、集合进行操作,而是创建一个临时数组、集合,将原数组、集合中的数据复制给临时数组、集合,然后再对这个临时数组进行修改操作,执行完毕后在将临时数组、集合赋值给原数组、集合。这个操作时为了保证迭代稳定性。这里的操作要保证互斥(加锁)。

这段代码不长,首先是通过 instanceInfo 来获取租约信息,那么先来看看LeaseInfo中有哪些元素,代码如下:

@JsonRootName("leaseInfo")
public class LeaseInfo {

    // 续约时间间隔
    public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
    // 如果 90 s(默认)没有发心跳,服务端就认为这个客户端挂了,就可以在服务端删除客户端的实例信息
    public static final int DEFAULT_LEASE_DURATION = 90;

    // Client settings
    private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
    private int durationInSecs = DEFAULT_LEASE_DURATION;

    // Server populated
    private long registrationTimestamp;
    private long lastRenewalTimestamp;
    private long evictionTimestamp;
    private long serviceUpTimestamp;
    // 省略部分代码
}

在ApplicationInfoManager#refreshLeaseInfoIfRequired方法中,还会获取租约过期间隔信息和租约续约的时间间隔,然后继续判断这两个是否符合标准。当符合标准的时候,便会重新设置租约信息和dirty,这里设置dirty是为一开始提到的InstanceInfoReplicator#run方法的后续做准备的,这里先看下设置dirty的方法,代码如下:

public synchronized void setIsDirty() {
    isInstanceInfoDirty = true;
    lastDirtyTimestamp = System.currentTimeMillis();
}

方法很简单,就是把dirty设置为true,并设置了一个时间戳,以备后续之用,这时就可以回顾一下InstanceInfoReplicator#run方法,代码如下:

public void run() {
    try {
        discoveryClient.refreshInstanceInfo();

        // 获取 dirty 的时间戳
        Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
        if (dirtyTimestamp != null) {
            discoveryClient.register();
            // 重置 dirty
           instanceInfo.unsetIsDirty(dirtyTimestamp);
        }
    } catch (Throwable t) {
        logger.warn("There was a problem with the instance info replicator", t);
    } finally {
        Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
        scheduledPeriodicRef.set(next);
    }
}

public synchronized void unsetIsDirty(long unsetDirtyTimestamp) {
    // 如果 lastDirtyTimestamp <= unsetDirtyTimestamp ,表示前面的更新时成立的
    if (lastDirtyTimestamp <= unsetDirtyTimestamp) {
        isInstanceInfoDirty = false;
    } else {
    }
}

在前面设置dirty的时候,便设置了时间戳,因此这里的dirtyTimestamp便不会为null,所以这是便会执行discoveryClient.register()方法,当注册完毕后,便会执行finally中的任务。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值