blob: f1ea6b17debdd6d2935b2a52cad5229c4cd42a63 [file] [log] [blame]
/*
* 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.skywalking.oap.server.core.hierarchy;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.CoreModuleConfig;
import org.apache.skywalking.oap.server.core.analysis.Layer;
import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
import org.apache.skywalking.oap.server.core.config.HierarchyDefinitionService;
import org.apache.skywalking.oap.server.core.hierarchy.instance.InstanceHierarchyRelation;
import org.apache.skywalking.oap.server.core.hierarchy.service.ServiceHierarchyRelation;
import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
import org.apache.skywalking.oap.server.core.query.type.Service;
import org.apache.skywalking.oap.server.core.source.SourceReceiver;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.util.RunnableWithExceptionProtection;
@Slf4j
public class HierarchyService implements org.apache.skywalking.oap.server.library.module.Service {
private final ModuleManager moduleManager;
private final boolean isEnableHierarchy;
private SourceReceiver sourceReceiver;
private MetadataQueryService metadataQueryService;
private Map<String, Map<String, HierarchyDefinitionService.MatchingRule>> hierarchyDefinition;
public HierarchyService(ModuleManager moduleManager, CoreModuleConfig moduleConfig) {
this.moduleManager = moduleManager;
this.isEnableHierarchy = moduleConfig.isEnableHierarchy();
}
private SourceReceiver getSourceReceiver() {
if (sourceReceiver == null) {
this.sourceReceiver = moduleManager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
}
return sourceReceiver;
}
private Map<String, Map<String, HierarchyDefinitionService.MatchingRule>> getHierarchyDefinition() {
if (hierarchyDefinition == null) {
hierarchyDefinition = moduleManager.find(CoreModule.NAME)
.provider()
.getService(HierarchyDefinitionService.class).getHierarchyDefinition();
}
return hierarchyDefinition;
}
private MetadataQueryService getMetadataQueryService() {
if (metadataQueryService == null) {
this.metadataQueryService = moduleManager.find(CoreModule.NAME)
.provider()
.getService(MetadataQueryService.class);
}
return metadataQueryService;
}
/**
* Build the hierarchy relation between the 2 services. the `serviceLayer` and `relatedServiceLayer` hierarchy
* relations should be defined in `config/hierarchy-definition.yml`.
*
* @param upperServiceName the name of the service
* @param upperServiceLayer the layer of the service
* @param lowerServiceName the name of the lower service
* @param lowerServiceLayer the layer of the lower service
*/
public void toServiceHierarchyRelation(String upperServiceName,
Layer upperServiceLayer,
String lowerServiceName,
Layer lowerServiceLayer) {
if (!this.isEnableHierarchy) {
return;
}
Map<String, HierarchyDefinitionService.MatchingRule> lowerLayers = getHierarchyDefinition().get(upperServiceLayer.name());
if (lowerLayers == null || !lowerLayers.containsKey(lowerServiceLayer.name())) {
log.error("upperServiceLayer " + upperServiceLayer.name() + " or lowerServiceLayer " + lowerServiceLayer.name()
+ " is not defined in hierarchy-definition.yml.");
return;
}
autoMatchingServiceRelation(upperServiceName, upperServiceLayer, lowerServiceName, lowerServiceLayer);
}
/**
* Build the hierarchy relation between the 2 instances. the `serviceLayer` and `relatedServiceLayer` hierarchy
* relations should be defined in `config/hierarchy-definition.yml`.
*
* @param upperInstanceName the name of the upper instance
* @param upperServiceName the name of the upper service
* @param upperServiceLayer the layer of the upper service
* @param lowerInstanceName the name of the lower related instance
* @param lowerServiceName the name of the lower related service
* @param lowerServiceLayer the layer of the lower related service
*/
public void toInstanceHierarchyRelation(String upperInstanceName,
String upperServiceName,
Layer upperServiceLayer,
String lowerInstanceName,
String lowerServiceName,
Layer lowerServiceLayer) {
if (!this.isEnableHierarchy) {
return;
}
Map<String, HierarchyDefinitionService.MatchingRule> lowerLayers = getHierarchyDefinition().get(upperServiceLayer.name());
if (lowerLayers == null || !lowerLayers.containsKey(lowerServiceLayer.name())) {
log.error("upperServiceLayer " + upperServiceLayer.name() + " or lowerServiceLayer " + lowerServiceLayer.name()
+ " is not defined in hierarchy-definition.yml.");
return;
}
buildInstanceHierarchyRelation(upperInstanceName, upperServiceName, upperServiceLayer, lowerInstanceName,
lowerServiceName, lowerServiceLayer);
}
public void startAutoMatchingServiceHierarchy() {
if (!this.isEnableHierarchy) {
return;
}
Executors.newSingleThreadScheduledExecutor()
.scheduleWithFixedDelay(
new RunnableWithExceptionProtection(this::autoMatchingServiceRelation, t -> log.error(
"Scheduled auto matching service hierarchy from service traffic failure.", t)), 30, 20, TimeUnit.SECONDS);
}
private void autoMatchingServiceRelation(String upperServiceName,
Layer upperServiceLayer,
String lowerServiceName,
Layer lowerServiceLayer) {
ServiceHierarchyRelation serviceHierarchy = new ServiceHierarchyRelation();
serviceHierarchy.setServiceName(upperServiceName);
serviceHierarchy.setServiceLayer(upperServiceLayer);
serviceHierarchy.setRelatedServiceName(lowerServiceName);
serviceHierarchy.setRelatedServiceLayer(lowerServiceLayer);
serviceHierarchy.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
this.getSourceReceiver().receive(serviceHierarchy);
}
private void buildInstanceHierarchyRelation(String upperInstanceName,
String upperServiceName,
Layer upperServiceLayer,
String lowerInstanceName,
String lowerServiceName,
Layer lowerServiceLayer) {
InstanceHierarchyRelation instanceHierarchy = new InstanceHierarchyRelation();
instanceHierarchy.setInstanceName(upperInstanceName);
instanceHierarchy.setServiceName(upperServiceName);
instanceHierarchy.setServiceLayer(upperServiceLayer);
instanceHierarchy.setRelatedInstanceName(lowerInstanceName);
instanceHierarchy.setRelatedServiceName(lowerServiceName);
instanceHierarchy.setRelatedServiceLayer(lowerServiceLayer);
instanceHierarchy.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
this.getSourceReceiver().receive(instanceHierarchy);
}
private void autoMatchingServiceRelation() {
List<Service> allServices = getMetadataQueryService().listAllServices()
.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
if (allServices.size() > 1) {
for (int i = 0; i < allServices.size(); i++) {
for (int j = i + 1; j < allServices.size(); j++) {
Service service = allServices.get(i);
Service comparedService = allServices.get(j);
String serviceLayer = service.getLayers().iterator().next();
String comparedServiceLayer = comparedService.getLayers().iterator().next();
Map<String, HierarchyDefinitionService.MatchingRule> lowerLayers = getHierarchyDefinition().get(
serviceLayer);
Map<String, HierarchyDefinitionService.MatchingRule> comparedLowerLayers = getHierarchyDefinition().get(
comparedServiceLayer);
if (lowerLayers != null && lowerLayers.get(comparedServiceLayer) != null) {
try {
if (lowerLayers.get(comparedServiceLayer)
.getClosure()
.call(service, comparedService)) {
autoMatchingServiceRelation(service.getName(), Layer.nameOf(serviceLayer),
comparedService.getName(),
Layer.nameOf(comparedServiceLayer)
);
}
} catch (Throwable e) {
log.error(
"Auto matching service hierarchy from service traffic failure. Upper layer {}, lower layer {}, closure{}",
serviceLayer,
comparedServiceLayer,
lowerLayers.get(comparedServiceLayer).getExpression(), e
);
}
} else if (comparedLowerLayers != null && comparedLowerLayers.get(serviceLayer) != null) {
try {
if (comparedLowerLayers.get(serviceLayer)
.getClosure()
.call(comparedService, service)) {
autoMatchingServiceRelation(
comparedService.getName(),
Layer.nameOf(comparedServiceLayer),
service.getName(),
Layer.nameOf(serviceLayer)
);
}
} catch (Throwable e) {
log.error(
"Auto matching service hierarchy from service traffic failure. Upper layer {}, lower layer {}, closure{}",
comparedServiceLayer,
serviceLayer,
comparedLowerLayers.get(serviceLayer).getExpression(), e
);
}
}
}
}
}
}
}