| /* |
| * 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); |
| } |
| } |