diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java index b05408caacf..61eaf54d4ec 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/AbstractServiceDiscovery.java @@ -35,13 +35,14 @@ import org.apache.dubbo.registry.client.metadata.store.MetaCacheManager; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_EXPIRE; import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_INFO_CACHE_SIZE; @@ -50,7 +51,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.METADATA_INFO_CACHE_SIZE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_LOCAL_FILE_CACHE_ENABLED; import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_LOAD_METADATA; import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY; import static org.apache.dubbo.metadata.RevisionResolver.EMPTY_REVISION; @@ -70,7 +70,7 @@ public abstract class AbstractServiceDiscovery implements ServiceDiscovery { protected volatile ServiceInstance serviceInstance; protected volatile MetadataInfo metadataInfo; protected final ConcurrentHashMap metadataInfos = new ConcurrentHashMap<>(); - protected final ScheduledFuture refreshCacheFuture; + protected volatile ScheduledFuture refreshCacheFuture; protected MetadataReport metadataReport; protected String metadataType; protected final MetaCacheManager metaCacheManager; @@ -110,36 +110,44 @@ private AbstractServiceDiscovery(ApplicationModel applicationModel, String servi registryURL.getParameter(METADATA_INFO_CACHE_EXPIRE_KEY, DEFAULT_METADATA_INFO_CACHE_EXPIRE); int metadataInfoCacheSize = registryURL.getParameter(METADATA_INFO_CACHE_SIZE_KEY, DEFAULT_METADATA_INFO_CACHE_SIZE); + startRefreshCache(metadataInfoCacheExpireTime / 2, metadataInfoCacheSize, metadataInfoCacheExpireTime); + } + + private void removeExpiredMetadataInfo(int metadataInfoCacheSize, int metadataInfoCacheExpireTime) { + Long nextTime = null; + // Cache cleanup is only required when the cache size exceeds the cache limit. + if (metadataInfos.size() > metadataInfoCacheSize) { + List values = new ArrayList<>(metadataInfos.values()); + // Place the earliest data at the front + values.sort(Comparator.comparingLong(MetadataInfoStat::getUpdateTime)); + for (MetadataInfoStat v : values) { + long time = System.currentTimeMillis() - v.getUpdateTime(); + if (time > metadataInfoCacheExpireTime) { + metadataInfos.remove(v.metadataInfo.getRevision(), v); + } else { + // Calculate how long it will take for the next task to start + nextTime = metadataInfoCacheExpireTime - time; + break; + } + } + } + // If there is no metadata to clean up this time, the next task will start within half of the cache expiration + // time. + startRefreshCache( + nextTime == null ? metadataInfoCacheExpireTime / 2 : nextTime, + metadataInfoCacheSize, + metadataInfoCacheExpireTime); + } + + private void startRefreshCache(long nextTime, int metadataInfoCacheSize, int metadataInfoCacheExpireTime) { this.refreshCacheFuture = applicationModel .getFrameworkModel() .getBeanFactory() .getBean(FrameworkExecutorRepository.class) .getSharedScheduledExecutor() - .scheduleAtFixedRate( - () -> { - try { - while (metadataInfos.size() > metadataInfoCacheSize) { - AtomicReference oldestRevision = new AtomicReference<>(); - AtomicReference oldestStat = new AtomicReference<>(); - metadataInfos.forEach((k, v) -> { - if (System.currentTimeMillis() - v.getUpdateTime() > metadataInfoCacheExpireTime - && (oldestStat.get() == null - || oldestStat.get().getUpdateTime() > v.getUpdateTime())) { - oldestRevision.set(k); - oldestStat.set(v); - } - }); - if (oldestStat.get() != null) { - metadataInfos.remove(oldestRevision.get(), oldestStat.get()); - } - } - } catch (Throwable t) { - logger.error( - INTERNAL_ERROR, "", "", "Error occurred when clean up metadata info cache.", t); - } - }, - metadataInfoCacheExpireTime / 2, - metadataInfoCacheExpireTime / 2, + .schedule( + () -> removeExpiredMetadataInfo(metadataInfoCacheSize, metadataInfoCacheExpireTime), + nextTime, TimeUnit.MILLISECONDS); }