blob: a53e47280a2d7585b80c83594ccc5d3b450a2b9e [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.dubbo.rpc.cluster.router.mesh.route;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
import org.apache.dubbo.rpc.cluster.router.state.BitList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME;
public class MeshRuleCache<T> {
private final List<String> appList;
private final Map<String, VsDestinationGroup> appToVDGroup;
private final Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap;
private final BitList<Invoker<T>> unmatchedInvokers;
private MeshRuleCache(
List<String> appList,
Map<String, VsDestinationGroup> appToVDGroup,
Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap,
BitList<Invoker<T>> unmatchedInvokers) {
this.appList = appList;
this.appToVDGroup = appToVDGroup;
this.totalSubsetMap = totalSubsetMap;
this.unmatchedInvokers = unmatchedInvokers;
}
public List<String> getAppList() {
return appList;
}
public Map<String, VsDestinationGroup> getAppToVDGroup() {
return appToVDGroup;
}
public Map<String, Map<String, BitList<Invoker<T>>>> getTotalSubsetMap() {
return totalSubsetMap;
}
public BitList<Invoker<T>> getUnmatchedInvokers() {
return unmatchedInvokers;
}
public VsDestinationGroup getVsDestinationGroup(String appName) {
return appToVDGroup.get(appName);
}
public BitList<Invoker<T>> getSubsetInvokers(String appName, String subset) {
Map<String, BitList<Invoker<T>>> appToSubSets = totalSubsetMap.get(appName);
if (CollectionUtils.isNotEmptyMap(appToSubSets)) {
BitList<Invoker<T>> subsetInvokers = appToSubSets.get(subset);
if (CollectionUtils.isNotEmpty(subsetInvokers)) {
return subsetInvokers;
}
}
return BitList.emptyList();
}
public boolean containsRule() {
return !totalSubsetMap.isEmpty();
}
public static <T> MeshRuleCache<T> build(
String protocolServiceKey,
BitList<Invoker<T>> invokers,
Map<String, VsDestinationGroup> vsDestinationGroupMap) {
if (CollectionUtils.isNotEmptyMap(vsDestinationGroupMap)) {
BitList<Invoker<T>> unmatchedInvokers = new BitList<>(invokers.getOriginList(), true);
Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap = new HashMap<>();
for (Invoker<T> invoker : invokers) {
String remoteApplication = invoker.getUrl().getRemoteApplication();
if (StringUtils.isEmpty(remoteApplication) || INVALID_APP_NAME.equals(remoteApplication)) {
unmatchedInvokers.add(invoker);
continue;
}
VsDestinationGroup vsDestinationGroup = vsDestinationGroupMap.get(remoteApplication);
if (vsDestinationGroup == null) {
unmatchedInvokers.add(invoker);
continue;
}
Map<String, BitList<Invoker<T>>> subsetMap =
totalSubsetMap.computeIfAbsent(remoteApplication, (k) -> new HashMap<>());
boolean matched = false;
for (DestinationRule destinationRule : vsDestinationGroup.getDestinationRuleList()) {
DestinationRuleSpec destinationRuleSpec = destinationRule.getSpec();
List<Subset> subsetList = destinationRuleSpec.getSubsets();
for (Subset subset : subsetList) {
String subsetName = subset.getName();
List<Invoker<T>> subsetInvokers = subsetMap.computeIfAbsent(
subsetName, (k) -> new BitList<>(invokers.getOriginList(), true));
Map<String, String> labels = subset.getLabels();
if (isLabelMatch(invoker.getUrl(), protocolServiceKey, labels)) {
subsetInvokers.add(invoker);
matched = true;
}
}
}
if (!matched) {
unmatchedInvokers.add(invoker);
}
}
return new MeshRuleCache<>(
new LinkedList<>(vsDestinationGroupMap.keySet()),
Collections.unmodifiableMap(vsDestinationGroupMap),
Collections.unmodifiableMap(totalSubsetMap),
unmatchedInvokers);
} else {
return new MeshRuleCache<>(
Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), invokers);
}
}
public static <T> MeshRuleCache<T> emptyCache() {
return new MeshRuleCache<>(
Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), BitList.emptyList());
}
protected static boolean isLabelMatch(URL url, String protocolServiceKey, Map<String, String> inputMap) {
if (inputMap == null || inputMap.size() == 0) {
return true;
}
for (Map.Entry<String, String> entry : inputMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String originMapValue = url.getOriginalServiceParameter(protocolServiceKey, key);
if (!value.equals(originMapValue)) {
return false;
}
}
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MeshRuleCache<?> ruleCache = (MeshRuleCache<?>) o;
return Objects.equals(appList, ruleCache.appList)
&& Objects.equals(appToVDGroup, ruleCache.appToVDGroup)
&& Objects.equals(totalSubsetMap, ruleCache.totalSubsetMap)
&& Objects.equals(unmatchedInvokers, ruleCache.unmatchedInvokers);
}
@Override
public int hashCode() {
return Objects.hash(appList, appToVDGroup, totalSubsetMap, unmatchedInvokers);
}
}