| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.servicecomb.serviceregistry.diagnosis.instance; |
| |
| import java.time.Clock; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import org.apache.servicecomb.foundation.common.utils.TimeUtils; |
| import org.apache.servicecomb.registry.api.registry.MicroserviceInstance; |
| import org.apache.servicecomb.registry.api.registry.MicroserviceInstances; |
| import org.apache.servicecomb.registry.consumer.AppManager; |
| import org.apache.servicecomb.registry.consumer.MicroserviceManager; |
| import org.apache.servicecomb.registry.consumer.MicroserviceVersions; |
| import org.apache.servicecomb.registry.consumer.StaticMicroserviceVersions; |
| import org.apache.servicecomb.registry.definition.DefinitionConst; |
| import org.apache.servicecomb.serviceregistry.RegistryUtils; |
| import org.apache.servicecomb.serviceregistry.diagnosis.Status; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import io.vertx.core.json.Json; |
| |
| public class InstanceCacheChecker { |
| private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCacheChecker.class); |
| |
| Clock clock = TimeUtils.getSystemDefaultZoneClock(); |
| |
| private final AppManager appManager; |
| |
| private final Set<Status> statuses = new HashSet<>(); |
| |
| private final InstanceCacheSummary instanceCacheSummary = new InstanceCacheSummary(); |
| |
| public InstanceCacheChecker(AppManager appManager) { |
| this.appManager = appManager; |
| } |
| |
| public InstanceCacheSummary check() { |
| instanceCacheSummary.setAppId(RegistryUtils.getMicroservice().getAppId()); |
| instanceCacheSummary.setMicroserviceName(RegistryUtils.getMicroservice().getServiceName()); |
| instanceCacheSummary.setTimestamp(clock.millis()); |
| |
| for (MicroserviceManager microserviceManager : appManager.getApps().values()) { |
| for (MicroserviceVersions microserviceVersions : microserviceManager.getVersionsByName().values()) { |
| if (microserviceVersions instanceof StaticMicroserviceVersions) { |
| continue; |
| } |
| InstanceCacheResult instanceCacheResult = check(microserviceVersions); |
| addInstanceCacheResult(instanceCacheResult); |
| } |
| } |
| |
| generateStatus(); |
| |
| return instanceCacheSummary; |
| } |
| |
| private void addInstanceCacheResult(InstanceCacheResult instanceCacheResult) { |
| statuses.add(instanceCacheResult.getStatus()); |
| instanceCacheSummary.getProducers().add(instanceCacheResult); |
| } |
| |
| protected InstanceCacheResult check(MicroserviceVersions microserviceVersions) { |
| InstanceCacheResult instanceCacheResult = new InstanceCacheResult(); |
| instanceCacheResult.setAppId(microserviceVersions.getAppId()); |
| instanceCacheResult.setMicroserviceName(microserviceVersions.getMicroserviceName()); |
| instanceCacheResult.setPulledInstances(microserviceVersions.getPulledInstances()); |
| |
| MicroserviceInstances microserviceInstances = RegistryUtils |
| .findServiceInstances(microserviceVersions.getAppId(), |
| microserviceVersions.getMicroserviceName(), |
| DefinitionConst.VERSION_RULE_ALL); |
| if (microserviceInstances == null) { |
| instanceCacheResult.setStatus(Status.UNKNOWN); |
| instanceCacheResult.setDetail("failed to find instances from service center"); |
| return instanceCacheResult; |
| } |
| if (microserviceInstances.isMicroserviceNotExist()) { |
| // no problem, will be deleted from MicroserviceManager in next pull |
| instanceCacheResult.setStatus(Status.UNKNOWN); |
| instanceCacheResult.setDetail("microservice is not exist anymore, will be deleted from memory in next pull"); |
| return instanceCacheResult; |
| } |
| |
| if (!Objects.equals(microserviceInstances.getRevision(), microserviceVersions.getRevision())) { |
| // maybe not pull, wait for next pull we get the same revision |
| instanceCacheResult.setStatus(Status.UNKNOWN); |
| instanceCacheResult.setDetail(String.format( |
| "revision is different, will be synchronized in next pull. local revision=%s, remote revision=%s", |
| microserviceVersions.getRevision(), microserviceInstances.getRevision())); |
| // better to change revision and more likely to find the correct instances in next pull. |
| microserviceVersions.setRevision(null); |
| return instanceCacheResult; |
| } |
| |
| // compare all instances |
| List<MicroserviceInstance> remoteInstances = microserviceInstances.getInstancesResponse().getInstances(); |
| remoteInstances.sort(Comparator.comparing(MicroserviceInstance::getInstanceId)); |
| String local = Json.encode(microserviceVersions.getPulledInstances()); |
| String remote = Json.encode(remoteInstances); |
| if (local.equals(remote)) { |
| LOGGER.info("instance cache match. appId={}, microservice={}.\n" |
| + "current cache: {}\n", |
| microserviceVersions.getAppId(), |
| microserviceVersions.getMicroserviceName(), |
| remoteInstances); |
| instanceCacheResult.setStatus(Status.NORMAL); |
| return instanceCacheResult; |
| } |
| |
| LOGGER.error("instance cache not match. appId={}, microservice={}.\n" |
| + "local cache: {}\n" |
| + "remote cache: {}", |
| microserviceVersions.getAppId(), |
| microserviceVersions.getMicroserviceName(), |
| microserviceVersions.getPulledInstances().toString(), |
| remoteInstances); |
| instanceCacheResult.setStatus(Status.ABNORMAL); |
| instanceCacheResult.setDetail("instance cache not match"); |
| |
| // auto fix, will do a full pull request when invoke MicroserviceVersions.pullInstances |
| microserviceVersions.setRevision(null); |
| |
| return instanceCacheResult; |
| } |
| |
| protected void generateStatus() { |
| if (statuses.contains(Status.ABNORMAL)) { |
| instanceCacheSummary.setStatus(Status.ABNORMAL); |
| } else if (statuses.contains(Status.UNKNOWN)) { |
| instanceCacheSummary.setStatus(Status.UNKNOWN); |
| } else { |
| instanceCacheSummary.setStatus(Status.NORMAL); |
| } |
| } |
| } |