Eureka源码分析(九)清理过期租约

下面我们来说说eureka的过期机制。其实这篇文章主要介绍的是AbstractInstanceRegistry的内部类EvictionTask。我们知道AbstractInstanceRegistry是包括了所有注册到EurekaServer上的实例。而EvictionTask就是清理过期租约的定时任务。在 Eureka-Server 启动时,初始化 EvictionTask 定时执行。

protected void postInit() {
    renewsLastMin.start();
    // 初始化 清理租约过期任务
    if (evictionTaskRef.get() != null) {
        evictionTaskRef.get().cancel();
    }
    evictionTaskRef.set(new EvictionTask());
    evictionTimer.schedule(evictionTaskRef.get(),
            serverConfig.getEvictionIntervalTimerInMs(),
            serverConfig.getEvictionIntervalTimerInMs());
}

配置 eureka.evictionIntervalTimerInMs ,清理租约过期任务执行频率,单位:毫秒。默认,60000 毫秒。
看下EvictionTask的具体实现

class EvictionTask extends TimerTask {

    /**
     * 最后任务执行时间
     */
    private final AtomicLong lastExecutionNanosRef = new AtomicLong(0L);

    @Override
    public void run() {
        try {
            // 获取 补偿时间毫秒数
            long compensationTimeMs = getCompensationTimeMs();
            logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);
            // 清理过期租约逻辑
            evict(compensationTimeMs);
        } catch (Throwable e) {
            logger.error("Could not run the evict task", e);
        }
    }

    /**
     * compute a compensation time defined as the actual time this task was executed since the prev iteration,
     * vs the configured amount of time for execution. This is useful for cases where changes in time (due to
     * clock skew or gc for example) causes the actual eviction task to execute later than the desired time
     * according to the configured cycle.
     */
    long getCompensationTimeMs() {
        long currNanos = getCurrentTimeNano();
        long lastNanos = lastExecutionNanosRef.getAndSet(currNanos);
        if (lastNanos == 0L) {
            return 0L;
        }
        long elapsedMs = TimeUnit.NANOSECONDS.toMillis(currNanos - lastNanos);
        long compensationTime = elapsedMs - serverConfig.getEvictionIntervalTimerInMs();
        return compensationTime <= 0L ? 0L : compensationTime;
    }

    long getCurrentTimeNano() {  // for testing
        return System.nanoTime();
    }

}

调用 evict方法,执行清理过期租约逻辑

public void evict(long additionalLeaseMs) {
    logger.debug("Running the evict task");

    if (!isLeaseExpirationEnabled()) {
        logger.debug("DS: lease expiration is currently disabled.");
        return;
    }

    // 获得 所有过期的租约
    // We collect first all expired items, to evict them in random order. For large eviction sets,
    // if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,
    // the impact should be evenly distributed across all applications.
    List> expiredLeases = new ArrayList<>();
    for (Entry>> groupEntry : registry.entrySet()) {
        Map> leaseMap = groupEntry.getValue();
        if (leaseMap != null) {
            for (Entry> leaseEntry : leaseMap.entrySet()) {
                Lease lease = leaseEntry.getValue();
                if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) { // 过期
                    expiredLeases.add(lease);
                }
            }
        }
    }

    // 计算 最大允许清理租约数量
    // To compensate for GC pauses or drifting local time, we need to use current registry size as a base for
    // triggering self-preservation. Without that we would wipe out full registry.
    int registrySize = (int) getLocalRegistrySize();
    int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());
    int evictionLimit = registrySize - registrySizeThreshold;

    // 计算 清理租约数量
    int toEvict = Math.min(expiredLeases.size(), evictionLimit);
    if (toEvict > 0) {
        logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit);

        // 逐个过期
        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < toEvict; i++) {
            // Pick a random item (Knuth shuffle algorithm)
            int next = i + random.nextInt(expiredLeases.size() - i);
            Collections.swap(expiredLeases, i, next);
            Lease lease = expiredLeases.get(i);

            String appName = lease.getHolder().getAppName();
            String id = lease.getHolder().getId();
            EXPIRED.increment();
            logger.warn("DS: Registry: expired lease for {}/{}", appName, id);
            internalCancel(appName, id, false);
        }
    }
}

eureka清理过期租约的过程就完成了。

你可能感兴趣的:(Eureka源码分析(九)清理过期租约)