diff --git a/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml b/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml index b262a00b4..99c7e1c18 100644 --- a/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml +++ b/polaris-common/polaris-config-default/src/main/resources/conf/default-config.yml @@ -167,6 +167,8 @@ consumer: - isolatedRouter #描述: 服务路由链 chain: + # 命名空间就近路由 + - namespaceRouter # 泳道路由 - laneRouter # 元数据路由 @@ -191,10 +193,10 @@ consumer: #描述:规则路由降级策略。all(降级返回所有的节点),none(不降级) failoverType: all nearbyBasedRouter: - #描述: 就近路由的最小匹配级别。region(大区)、zone(区域)、campus(园区) - matchLevel: zone + #描述: 就近路由的最小匹配级别。ALL(全部)、REGION(大区)、ZONE(区域)、CAMPUS(园区) + matchLevel: ZONE #描述: 最大匹配级别 - maxMatchLevel: all + maxMatchLevel: ALL #描述: 强制就近 strictNearby: false #描述: 全部实例不健康时是否降级其他地域 diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java index cfa7cc103..bdef22537 100644 --- a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java @@ -19,6 +19,7 @@ import com.tencent.polaris.api.config.plugin.PluginConfig; import com.tencent.polaris.api.config.verify.Verifier; + import java.util.List; /** @@ -44,6 +45,8 @@ public interface ServiceRouterConfig extends PluginConfig, Verifier { String DEFAULT_ROUTER_CANARY = "canaryRouter"; String DEFAULT_ROUTER_LANE = "laneRouter"; + + String DEFAULT_ROUTER_NAMESPACE = "namespaceRouter"; /** diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java index 58e83dccd..beb28151b 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java @@ -193,7 +193,11 @@ public Map getServiceMetadata() { } public void setServiceMetadata(Map serviceMetadata) { - this.serviceMetadata = serviceMetadata; + if (serviceMetadata != null) { + this.serviceMetadata = serviceMetadata; + } else { + this.serviceMetadata = new HashMap<>(); + } } @Override diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java index 97dd7c5ea..ab605cbe1 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java @@ -2,7 +2,6 @@ import com.tencent.polaris.api.utils.StringUtils; -import java.util.Map; import java.util.Objects; /** @@ -33,6 +32,7 @@ public enum EventType { FAULT_DETECTING, SERVICE_CONTRACT, LANE_RULE, + NEARBY_ROUTE_RULE, } private final ServiceKey serviceKey; diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/NamespaceRouterFailoverType.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/NamespaceRouterFailoverType.java new file mode 100644 index 000000000..86f5f303c --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/NamespaceRouterFailoverType.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.api.rpc; + +/** + * 命名空间就近路由的降级类型 + * + * @author Haotian Zhang + */ +public enum NamespaceRouterFailoverType { + + /** + * 默认返回全量地址。优先保证服务调用正常 + */ + all, + /** + * 不降级,返回空地址列表 + */ + none +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CompareUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CompareUtils.java new file mode 100644 index 000000000..1f4ad160d --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CompareUtils.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.api.utils; + +/** + * Utils for comparing something. + * + * @author Haotian Zhang + */ +public class CompareUtils { + + public static boolean isWildcardMatcherSingle(String name) { + return StringUtils.equalsIgnoreCase(name, RuleUtils.MATCH_ALL) || StringUtils.isBlank(name); + } + + public static int compareSingleValue(String value1, String value2) { + boolean serviceWildcard1 = isWildcardMatcherSingle(value1); + boolean serviceWildcard2 = isWildcardMatcherSingle(value2); + if (serviceWildcard1 && serviceWildcard2) { + return 0; + } + if (serviceWildcard1) { + // 1 before 2 + return 1; + } + if (serviceWildcard2) { + // 1 before 2 + return -1; + } + return value1.compareTo(value2); + } + + public static int compareService(String namespace1, String service1, String namespace2, String service2) { + int nsResult = CompareUtils.compareSingleValue(namespace1, namespace2); + if (nsResult != 0) { + return nsResult; + } + return CompareUtils.compareSingleValue(service1, service2); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java index ee9760d55..d68dab035 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java @@ -18,6 +18,7 @@ package com.tencent.polaris.api.utils; +import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.TrieNode; import com.tencent.polaris.logging.LoggerFactory; import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup; @@ -319,4 +320,21 @@ private static boolean matchValueByValueType(boolean isMatchSource, String ruleM return allMetaMatched; } + public static boolean matchService(ServiceKey serviceKey, String namespace, String service) { + String inputNamespace = ""; + String inputService = ""; + if (null != serviceKey) { + inputNamespace = serviceKey.getNamespace(); + inputService = serviceKey.getService(); + } + if (StringUtils.isNotBlank(namespace) && !StringUtils.equals(namespace, RuleUtils.MATCH_ALL) && !StringUtils + .equals(inputNamespace, namespace)) { + return false; + } + if (StringUtils.isNotBlank(service) && !StringUtils.equals(service, RuleUtils.MATCH_ALL) && !StringUtils + .equals(inputService, service)) { + return false; + } + return true; + } } diff --git a/polaris-common/polaris-model/src/test/java/com/tencent/polaris/api/utils/CompareUtilsTest.java b/polaris-common/polaris-model/src/test/java/com/tencent/polaris/api/utils/CompareUtilsTest.java new file mode 100644 index 000000000..bc9f9fd81 --- /dev/null +++ b/polaris-common/polaris-model/src/test/java/com/tencent/polaris/api/utils/CompareUtilsTest.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.api.utils; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link CompareUtils} + * + * @author Haotian Zhang + */ +public class CompareUtilsTest { + + @Test + public void testIsWildcardMatcherSingle() { + assertThat(CompareUtils.isWildcardMatcherSingle("*")).isTrue(); + assertThat(CompareUtils.isWildcardMatcherSingle("")).isTrue(); + assertThat(CompareUtils.isWildcardMatcherSingle("abc")).isFalse(); + } + + @Test + public void testCompareSingleValue() { + assertThat(CompareUtils.compareSingleValue("*", "abc") > 0).isTrue(); + assertThat(CompareUtils.compareSingleValue("*", "*") == 0).isTrue(); + assertThat(CompareUtils.compareSingleValue("abc", "*") < 0).isTrue(); + assertThat(CompareUtils.compareSingleValue("abc", "def") < 0).isTrue(); + } + + @Test + public void testCompareService() { + assertThat(CompareUtils.compareService("*", "*", "*", "*") == 0).isTrue(); + assertThat(CompareUtils.compareService("*", "abc", "*", "*") < 0).isTrue(); + assertThat(CompareUtils.compareService("*", "abc", "abc", "*") > 0).isTrue(); + assertThat(CompareUtils.compareService("abc", "abc", "abc", "*") < 0).isTrue(); + assertThat(CompareUtils.compareService("abc", "abc", "abc", "def") < 0).isTrue(); + assertThat(CompareUtils.compareService("abc", "*", "abc", "def") > 0).isTrue(); + assertThat(CompareUtils.compareService("abc", "*", "*", "def") < 0).isTrue(); + } +} diff --git a/polaris-dependencies/pom.xml b/polaris-dependencies/pom.xml index 40e1608d8..ad02fe939 100644 --- a/polaris-dependencies/pom.xml +++ b/polaris-dependencies/pom.xml @@ -342,6 +342,11 @@ router-lane ${project.version} + + com.tencent.polaris + router-namespace + ${project.version} + com.tencent.polaris router-metadata diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/DefaultDiscoveryFlow.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/DefaultDiscoveryFlow.java index 4f193a643..e57a2fbf8 100644 --- a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/DefaultDiscoveryFlow.java +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/DefaultDiscoveryFlow.java @@ -25,34 +25,10 @@ import com.tencent.polaris.api.flow.DiscoveryFlow; import com.tencent.polaris.api.plugin.lossless.InstanceProperties; import com.tencent.polaris.api.plugin.lossless.LosslessPolicy; -import com.tencent.polaris.api.plugin.route.LocationLevel; -import com.tencent.polaris.api.plugin.server.CommonProviderRequest; -import com.tencent.polaris.api.plugin.server.CommonProviderResponse; -import com.tencent.polaris.api.plugin.server.CommonServiceContractRequest; -import com.tencent.polaris.api.plugin.server.ReportServiceContractRequest; -import com.tencent.polaris.api.plugin.server.ReportServiceContractResponse; -import com.tencent.polaris.api.plugin.server.ServerConnector; -import com.tencent.polaris.api.plugin.server.TargetServer; +import com.tencent.polaris.api.plugin.server.*; import com.tencent.polaris.api.pojo.BaseInstance; import com.tencent.polaris.api.pojo.RetStatus; -import com.tencent.polaris.api.rpc.GetAllInstancesRequest; -import com.tencent.polaris.api.rpc.GetHealthyInstancesRequest; -import com.tencent.polaris.api.rpc.GetServiceContractRequest; -import com.tencent.polaris.api.rpc.GetServiceRuleRequest; -import com.tencent.polaris.api.rpc.GetServicesRequest; -import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; -import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; -import com.tencent.polaris.api.rpc.InstanceRegisterRequest; -import com.tencent.polaris.api.rpc.InstanceRegisterResponse; -import com.tencent.polaris.api.rpc.InstancesFuture; -import com.tencent.polaris.api.rpc.InstancesResponse; -import com.tencent.polaris.api.rpc.RequestBaseEntity; -import com.tencent.polaris.api.rpc.ServiceCallResult; -import com.tencent.polaris.api.rpc.ServiceRuleResponse; -import com.tencent.polaris.api.rpc.ServicesResponse; -import com.tencent.polaris.api.rpc.UnWatchInstancesRequest; -import com.tencent.polaris.api.rpc.WatchInstancesRequest; -import com.tencent.polaris.api.rpc.WatchServiceResponse; +import com.tencent.polaris.api.rpc.*; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.client.api.SDKContext; @@ -60,6 +36,7 @@ import com.tencent.polaris.client.pojo.ServiceRuleByProto; import com.tencent.polaris.client.util.Utils; import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import org.slf4j.Logger; import java.util.List; @@ -338,9 +315,9 @@ private void enrichInstanceLocation(InstanceRegisterRequest request) { return; } - request.setRegion(sdkContext.getValueContext().getValue(LocationLevel.region.name())); - request.setZone(sdkContext.getValueContext().getValue(LocationLevel.zone.name())); - request.setCampus(sdkContext.getValueContext().getValue(LocationLevel.campus.name())); + request.setRegion(sdkContext.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name())); + request.setZone(sdkContext.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name())); + request.setCampus(sdkContext.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name())); } /** diff --git a/polaris-discovery/polaris-discovery-factory/pom.xml b/polaris-discovery/polaris-discovery-factory/pom.xml index c35151923..f66254698 100644 --- a/polaris-discovery/polaris-discovery-factory/pom.xml +++ b/polaris-discovery/polaris-discovery-factory/pom.xml @@ -79,6 +79,11 @@ router-lane ${project.version} + + com.tencent.polaris + router-namespace + ${project.version} + com.tencent.polaris diff --git a/polaris-examples/quickstart-example/quickstart-example-provider/src/main/java/com/tencent/polaris/quickstart/example/Provider.java b/polaris-examples/quickstart-example/quickstart-example-provider/src/main/java/com/tencent/polaris/quickstart/example/Provider.java index 3d330140e..d8e06fffc 100644 --- a/polaris-examples/quickstart-example/quickstart-example-provider/src/main/java/com/tencent/polaris/quickstart/example/Provider.java +++ b/polaris-examples/quickstart-example/quickstart-example-provider/src/main/java/com/tencent/polaris/quickstart/example/Provider.java @@ -22,7 +22,6 @@ import com.sun.net.httpserver.HttpServer; import com.tencent.polaris.api.config.Configuration; import com.tencent.polaris.api.core.ProviderAPI; -import com.tencent.polaris.api.plugin.route.LocationLevel; import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; import com.tencent.polaris.api.rpc.InstanceRegisterRequest; import com.tencent.polaris.api.rpc.InstanceRegisterResponse; @@ -30,6 +29,7 @@ import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import com.tencent.polaris.quickstart.example.utils.ProviderExampleUtils; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import java.io.IOException; import java.io.OutputStream; @@ -85,7 +85,7 @@ private static String providedInstanceId(String namespace, String service, Strin // do the instance register private static void register(String namespace, String service, String host, int port, - ProviderAPI providerAPI) { + ProviderAPI providerAPI) { InstanceRegisterRequest registerRequest = new InstanceRegisterRequest(); registerRequest.setNamespace(namespace); registerRequest.setService(service); @@ -103,7 +103,7 @@ private static void register(String namespace, String service, String host, int // do the instance deregister private static void deregister(String namespace, String service, String host, int port, - ProviderAPI providerAPI) { + ProviderAPI providerAPI) { InstanceDeregisterRequest deregisterRequest = new InstanceDeregisterRequest(); deregisterRequest.setNamespace(namespace); deregisterRequest.setService(service); @@ -155,9 +155,9 @@ public EchoServerHandler(SDKContext context, int localPort) { public void handle(HttpExchange exchange) throws IOException { Map location = new HashMap<>(); - location.put(LocationLevel.region.name(), context.getValueContext().getValue(LocationLevel.region.name())); - location.put(LocationLevel.zone.name(), context.getValueContext().getValue(LocationLevel.zone.name())); - location.put(LocationLevel.campus.name(), context.getValueContext().getValue(LocationLevel.campus.name())); + location.put(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name(), context.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name())); + location.put(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name(), context.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name())); + location.put(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name(), context.getValueContext().getValue(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name())); Map parameters = splitQuery(exchange.getRequestURI()); String echoValue = parameters.get("value"); diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java index cd65cdf57..a8a28629c 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java @@ -40,7 +40,6 @@ import com.tencent.polaris.api.plugin.location.LocationProvider; import com.tencent.polaris.api.plugin.lossless.LosslessPolicy; import com.tencent.polaris.api.plugin.registry.LocalRegistry; -import com.tencent.polaris.api.plugin.route.LocationLevel; import com.tencent.polaris.api.plugin.route.ServiceRouter; import com.tencent.polaris.api.plugin.server.ServerConnector; import com.tencent.polaris.api.plugin.stat.StatReporter; @@ -52,6 +51,7 @@ import com.tencent.polaris.client.util.NamedThreadFactory; import com.tencent.polaris.logging.LoggerFactory; import com.tencent.polaris.specification.api.v1.model.ModelProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import org.slf4j.Logger; import java.io.IOException; @@ -86,7 +86,7 @@ public class Extensions extends Destroyable { private final List statReporters = new ArrayList<>(); - private TraceReporter traceReporter; + private TraceReporter traceReporter; private Supplier plugins; @@ -195,8 +195,8 @@ public void init(Configuration config, Supplier plugins, ValueContext valueConte // 加载监控上报 loadStatReporters(plugins); - // 加载调用链上报 - loadTraceReporter(plugins); + // 加载调用链上报 + loadTraceReporter(plugins); // 加载优雅上下线插件 loadLosslessPolicies(config, plugins); @@ -237,9 +237,9 @@ private void initLocation(Configuration config, ValueContext valueContext) { LOG.info("locationProvider plugin {} not found location", provider.getName()); continue; } - valueContext.setValue(LocationLevel.region.name(), location.getRegion().getValue()); - valueContext.setValue(LocationLevel.zone.name(), location.getZone().getValue()); - valueContext.setValue(LocationLevel.campus.name(), location.getCampus().getValue()); + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name(), location.getRegion().getValue()); + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name(), location.getZone().getValue()); + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name(), location.getCampus().getValue()); valueContext.notifyAllForLocationReady(); break; } @@ -281,14 +281,14 @@ private void loadStatReporters(Supplier plugins) throws PolarisException { } } - private void loadTraceReporter(Supplier plugins) throws PolarisException { - if (configuration.getGlobal().getTraceReporter().isEnable()) { - Collection reporters = plugins.getPlugins(PluginTypes.TRACE_REPORTER.getBaseType()); - if (CollectionUtils.isNotEmpty(reporters)) { - traceReporter = (TraceReporter) reporters.iterator().next(); - } - } - } + private void loadTraceReporter(Supplier plugins) throws PolarisException { + if (configuration.getGlobal().getTraceReporter().isEnable()) { + Collection reporters = plugins.getPlugins(PluginTypes.TRACE_REPORTER.getBaseType()); + if (CollectionUtils.isNotEmpty(reporters)) { + traceReporter = (TraceReporter) reporters.iterator().next(); + } + } + } private void loadLosslessPolicies(Configuration config, Supplier plugins) throws PolarisException { if (!config.getProvider().getLossless().isEnable()) { @@ -435,8 +435,7 @@ private void addNodeToDrift(Plugin plugin, Node node, HttpServerAware httpServer pluginToNodes.put(plugin.getName(), node); if (!allowPortDrift) { nodeToDrift.put(node, allowPortDrift); - } - else { + } else { nodeToDrift.putIfAbsent(node, allowPortDrift); } } else { @@ -447,7 +446,7 @@ private void addNodeToDrift(Plugin plugin, Node node, HttpServerAware httpServer } else { nodeToDrift.remove(targetNode); nodeToDrift.put(node, targetAllowDrift); - for (Map.Entry entry: pluginToNodes.entrySet()) { + for (Map.Entry entry : pluginToNodes.entrySet()) { if (entry.getValue().equals(targetNode)) { pluginToNodes.put(entry.getKey(), node); } @@ -562,9 +561,9 @@ public List getLosslessPolicies() { return losslessPolicies; } - public TraceReporter getTraceReporter() { - return traceReporter; - } + public TraceReporter getTraceReporter() { + return traceReporter; + } @Override protected void doDestroy() { diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java deleted file mode 100644 index 223bf7797..000000000 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Polaris available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * 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 com.tencent.polaris.api.plugin.route; - -/** - * 地域类型 - */ -public enum LocationLevel { - campus, zone, region, all, -} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java index 034a565af..f7efd67cc 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java @@ -20,6 +20,7 @@ import com.tencent.polaris.api.pojo.*; import com.tencent.polaris.api.pojo.StatusDimension.Level; import com.tencent.polaris.api.rpc.MetadataFailoverType; +import com.tencent.polaris.api.rpc.NamespaceRouterFailoverType; import com.tencent.polaris.api.rpc.RuleBasedRouterFailoverType; import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup; @@ -60,6 +61,8 @@ public class RouteInfo { private MetadataFailoverType metadataFailoverType; //规则路由降级类型 private RuleBasedRouterFailoverType ruleBasedRouterFailoverType; + // 命名空间就近路由降级类型 + private NamespaceRouterFailoverType namespaceRouterFailoverType; //各个路由插件依赖的 metadata 参数 private final Map> routerMetadata = new HashMap<>(); @@ -297,4 +300,12 @@ public MetadataContainerGroup getMetadataContainerGroup() { public void setMetadataContainerGroup(MetadataContainerGroup metadataContainerGroup) { this.metadataContainerGroup = metadataContainerGroup; } + + public NamespaceRouterFailoverType getNamespaceRouterFailoverType() { + return namespaceRouterFailoverType; + } + + public void setNamespaceRouterFailoverType(NamespaceRouterFailoverType namespaceRouterFailoverType) { + this.namespaceRouterFailoverType = namespaceRouterFailoverType; + } } diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java index 31bd59570..9e7fdb04a 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java @@ -18,6 +18,8 @@ package com.tencent.polaris.api.plugin.route; import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; + import java.util.List; /** @@ -41,7 +43,7 @@ public RouteResult(List instances, State state) { } public RouteResult(List instances, - NextRouterInfo nextRouterInfo, int hashCode) { + NextRouterInfo nextRouterInfo, int hashCode) { this.instances = instances; this.nextRouterInfo = nextRouterInfo; this.hashCode = hashCode; @@ -80,12 +82,12 @@ public static class NextRouterInfo { private final State state; - private LocationLevel locationLevel; + private RoutingProto.NearbyRoutingConfig.LocationLevel locationLevel; /** * 最小的存在实例的级别 */ - private LocationLevel minAvailableLevel; + private RoutingProto.NearbyRoutingConfig.LocationLevel minAvailableLevel; public NextRouterInfo(State state) { this.state = state; @@ -95,19 +97,19 @@ public State getState() { return state; } - public LocationLevel getLocationLevel() { + public RoutingProto.NearbyRoutingConfig.LocationLevel getLocationLevel() { return locationLevel; } - public void setLocationLevel(LocationLevel locationLevel) { + public void setLocationLevel(RoutingProto.NearbyRoutingConfig.LocationLevel locationLevel) { this.locationLevel = locationLevel; } - public LocationLevel getMinAvailableLevel() { + public RoutingProto.NearbyRoutingConfig.LocationLevel getMinAvailableLevel() { return minAvailableLevel; } - public void setMinAvailableLevel(LocationLevel minAvailableLevel) { + public void setMinAvailableLevel(RoutingProto.NearbyRoutingConfig.LocationLevel minAvailableLevel) { this.minAvailableLevel = minAvailableLevel; } } diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/CircuitBreakerRuleDictionary.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/CircuitBreakerRuleDictionary.java index d42e58fae..41464e809 100644 --- a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/CircuitBreakerRuleDictionary.java +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/CircuitBreakerRuleDictionary.java @@ -17,165 +17,163 @@ package com.tencent.polaris.plugins.circuitbreaker.composite; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.regex.Pattern; - import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource; import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceRule; import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.CompareUtils; +import com.tencent.polaris.api.utils.RuleUtils; import com.tencent.polaris.plugins.circuitbreaker.composite.utils.CircuitBreakerUtils; -import com.tencent.polaris.plugins.circuitbreaker.composite.utils.MatchUtils; import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; +import java.util.*; +import java.util.function.Function; +import java.util.regex.Pattern; + import static com.tencent.polaris.plugins.circuitbreaker.composite.utils.MatchUtils.matchMethod; public class CircuitBreakerRuleDictionary { - private final Map>> allRules = new HashMap<>(); - - private final Function regexToPattern; - - private final Object updateLock = new Object(); - - public CircuitBreakerRuleDictionary(Function regexToPattern) { - this.regexToPattern = regexToPattern; - allRules.put(CircuitBreakerProto.Level.SERVICE, CacheBuilder.newBuilder().build()); - allRules.put(CircuitBreakerProto.Level.METHOD, CacheBuilder.newBuilder().build()); - allRules.put(CircuitBreakerProto.Level.INSTANCE, CacheBuilder.newBuilder().build()); - } - - public CircuitBreakerProto.CircuitBreakerRule lookupCircuitBreakerRule(Resource resource) { - synchronized (updateLock) { - Cache> serviceKeyListCache = allRules.get(resource.getLevel()); - if (null == serviceKeyListCache) { - return null; - } - return selectRule(resource, serviceKeyListCache.getIfPresent(resource.getService()), regexToPattern); - } - } - - private static CircuitBreakerProto.CircuitBreakerRule selectRule(Resource resource, - List sortedRules, Function regexToPattern) { - if (CollectionUtils.isEmpty(sortedRules)) { - return null; - } - for (CircuitBreakerProto.CircuitBreakerRule cbRule : sortedRules) { - CircuitBreakerProto.RuleMatcher ruleMatcher = cbRule.getRuleMatcher(); - CircuitBreakerProto.RuleMatcher.DestinationService destination = ruleMatcher.getDestination(); - if (!MatchUtils.matchService(resource.getService(), destination.getNamespace(), destination.getService())) { - continue; - } - CircuitBreakerProto.RuleMatcher.SourceService source = ruleMatcher.getSource(); - if (!MatchUtils.matchService(resource.getCallerService(), source.getNamespace(), source.getService())) { - continue; - } - boolean methodMatched = matchMethod(resource, destination.getMethod(), regexToPattern); - if (methodMatched) { - return cbRule; - } - } - return null; - } - - /** - * rule on server has been changed, clear all caches to make it pull again - * @param serviceKey target service - */ - public void onServiceChanged(ServiceKey serviceKey) { - synchronized (updateLock) { - clearRules(serviceKey); - } - } - - private void clearRules(ServiceKey serviceKey) { - allRules.get(CircuitBreakerProto.Level.SERVICE).invalidate(serviceKey); - allRules.get(CircuitBreakerProto.Level.METHOD).invalidate(serviceKey); - allRules.get(CircuitBreakerProto.Level.INSTANCE).invalidate(serviceKey); - } - - public void putServiceRule(ServiceKey serviceKey, ServiceRule serviceRule) { - synchronized (updateLock) { - if (null == serviceRule || null == serviceRule.getRule()) { - clearRules(serviceKey); - return; - } - CircuitBreakerProto.CircuitBreaker circuitBreaker = (CircuitBreakerProto.CircuitBreaker) serviceRule.getRule(); - List rules = circuitBreaker.getRulesList(); - List subServiceRules = new ArrayList<>(); - List subMethodRules = new ArrayList<>(); - List subInstanceRules = new ArrayList<>(); - for (CircuitBreakerProto.CircuitBreakerRule rule : rules) { - if (!rule.getEnable() || !CircuitBreakerUtils.checkRule(rule)) { - continue; - } - CircuitBreakerProto.Level level = rule.getLevel(); - switch (level) { - case SERVICE: - subServiceRules.add(rule); - break; - case INSTANCE: - subInstanceRules.add(rule); - break; - case METHOD: - subMethodRules.add(rule); - break; - } - } - subServiceRules = sortCircuitBreakerRules(subServiceRules); - subMethodRules = sortCircuitBreakerRules(subMethodRules); - subInstanceRules = sortCircuitBreakerRules(subInstanceRules); - allRules.get(CircuitBreakerProto.Level.SERVICE).put(serviceKey, subServiceRules); - allRules.get(CircuitBreakerProto.Level.METHOD).put(serviceKey, subMethodRules); - allRules.get(CircuitBreakerProto.Level.INSTANCE).put(serviceKey, subInstanceRules); - } - } - - private static List sortCircuitBreakerRules(List rules) { - if (CollectionUtils.isEmpty(rules)) { - return rules; - } - List outRules = new ArrayList<>(rules); - outRules.sort(new Comparator() { - @Override - public int compare(CircuitBreakerProto.CircuitBreakerRule rule1, CircuitBreakerProto.CircuitBreakerRule rule2) { - // 1. compare destination service - CircuitBreakerProto.RuleMatcher ruleMatcher1 = rule1.getRuleMatcher(); - String destNamespace1 = ruleMatcher1.getDestination().getNamespace(); - String destService1 = ruleMatcher1.getDestination().getService(); - String destMethod1 = ruleMatcher1.getDestination().getMethod().getValue().getValue(); - - CircuitBreakerProto.RuleMatcher ruleMatcher2 = rule2.getRuleMatcher(); - String destNamespace2 = ruleMatcher2.getDestination().getNamespace(); - String destService2 = ruleMatcher2.getDestination().getService(); - String destMethod2 = ruleMatcher2.getDestination().getMethod().getValue().getValue(); - - int svcResult = MatchUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); - if (svcResult != 0) { - return svcResult; - } - if (rule1.getLevel() == CircuitBreakerProto.Level.METHOD && rule1.getLevel() == rule2.getLevel()) { - int methodResult = MatchUtils.compareSingleValue(destMethod1, destMethod2); - if (methodResult != 0) { - return methodResult; - } - } - // 2. compare source service - String srcNamespace1 = ruleMatcher1.getSource().getNamespace(); - String srcService1 = ruleMatcher1.getSource().getService(); - String srcNamespace2 = ruleMatcher2.getSource().getNamespace(); - String srcService2 = ruleMatcher2.getSource().getService(); - return MatchUtils.compareService(srcNamespace1, srcService1, srcNamespace2, srcService2); - } - }); - return outRules; - } + private final Map>> allRules = new HashMap<>(); + + private final Function regexToPattern; + + private final Object updateLock = new Object(); + + public CircuitBreakerRuleDictionary(Function regexToPattern) { + this.regexToPattern = regexToPattern; + allRules.put(CircuitBreakerProto.Level.SERVICE, CacheBuilder.newBuilder().build()); + allRules.put(CircuitBreakerProto.Level.METHOD, CacheBuilder.newBuilder().build()); + allRules.put(CircuitBreakerProto.Level.INSTANCE, CacheBuilder.newBuilder().build()); + } + + public CircuitBreakerProto.CircuitBreakerRule lookupCircuitBreakerRule(Resource resource) { + synchronized (updateLock) { + Cache> serviceKeyListCache = allRules.get(resource.getLevel()); + if (null == serviceKeyListCache) { + return null; + } + return selectRule(resource, serviceKeyListCache.getIfPresent(resource.getService()), regexToPattern); + } + } + + private static CircuitBreakerProto.CircuitBreakerRule selectRule(Resource resource, + List sortedRules, Function regexToPattern) { + if (CollectionUtils.isEmpty(sortedRules)) { + return null; + } + for (CircuitBreakerProto.CircuitBreakerRule cbRule : sortedRules) { + CircuitBreakerProto.RuleMatcher ruleMatcher = cbRule.getRuleMatcher(); + CircuitBreakerProto.RuleMatcher.DestinationService destination = ruleMatcher.getDestination(); + if (!RuleUtils.matchService(resource.getService(), destination.getNamespace(), destination.getService())) { + continue; + } + CircuitBreakerProto.RuleMatcher.SourceService source = ruleMatcher.getSource(); + if (!RuleUtils.matchService(resource.getCallerService(), source.getNamespace(), source.getService())) { + continue; + } + boolean methodMatched = matchMethod(resource, destination.getMethod(), regexToPattern); + if (methodMatched) { + return cbRule; + } + } + return null; + } + + /** + * rule on server has been changed, clear all caches to make it pull again + * + * @param serviceKey target service + */ + public void onServiceChanged(ServiceKey serviceKey) { + synchronized (updateLock) { + clearRules(serviceKey); + } + } + + private void clearRules(ServiceKey serviceKey) { + allRules.get(CircuitBreakerProto.Level.SERVICE).invalidate(serviceKey); + allRules.get(CircuitBreakerProto.Level.METHOD).invalidate(serviceKey); + allRules.get(CircuitBreakerProto.Level.INSTANCE).invalidate(serviceKey); + } + + public void putServiceRule(ServiceKey serviceKey, ServiceRule serviceRule) { + synchronized (updateLock) { + if (null == serviceRule || null == serviceRule.getRule()) { + clearRules(serviceKey); + return; + } + CircuitBreakerProto.CircuitBreaker circuitBreaker = (CircuitBreakerProto.CircuitBreaker) serviceRule.getRule(); + List rules = circuitBreaker.getRulesList(); + List subServiceRules = new ArrayList<>(); + List subMethodRules = new ArrayList<>(); + List subInstanceRules = new ArrayList<>(); + for (CircuitBreakerProto.CircuitBreakerRule rule : rules) { + if (!rule.getEnable() || !CircuitBreakerUtils.checkRule(rule)) { + continue; + } + CircuitBreakerProto.Level level = rule.getLevel(); + switch (level) { + case SERVICE: + subServiceRules.add(rule); + break; + case INSTANCE: + subInstanceRules.add(rule); + break; + case METHOD: + subMethodRules.add(rule); + break; + } + } + subServiceRules = sortCircuitBreakerRules(subServiceRules); + subMethodRules = sortCircuitBreakerRules(subMethodRules); + subInstanceRules = sortCircuitBreakerRules(subInstanceRules); + allRules.get(CircuitBreakerProto.Level.SERVICE).put(serviceKey, subServiceRules); + allRules.get(CircuitBreakerProto.Level.METHOD).put(serviceKey, subMethodRules); + allRules.get(CircuitBreakerProto.Level.INSTANCE).put(serviceKey, subInstanceRules); + } + } + + private static List sortCircuitBreakerRules(List rules) { + if (CollectionUtils.isEmpty(rules)) { + return rules; + } + List outRules = new ArrayList<>(rules); + outRules.sort(new Comparator() { + @Override + public int compare(CircuitBreakerProto.CircuitBreakerRule rule1, CircuitBreakerProto.CircuitBreakerRule rule2) { + // 1. compare destination service + CircuitBreakerProto.RuleMatcher ruleMatcher1 = rule1.getRuleMatcher(); + String destNamespace1 = ruleMatcher1.getDestination().getNamespace(); + String destService1 = ruleMatcher1.getDestination().getService(); + String destMethod1 = ruleMatcher1.getDestination().getMethod().getValue().getValue(); + + CircuitBreakerProto.RuleMatcher ruleMatcher2 = rule2.getRuleMatcher(); + String destNamespace2 = ruleMatcher2.getDestination().getNamespace(); + String destService2 = ruleMatcher2.getDestination().getService(); + String destMethod2 = ruleMatcher2.getDestination().getMethod().getValue().getValue(); + + int svcResult = CompareUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); + if (svcResult != 0) { + return svcResult; + } + if (rule1.getLevel() == CircuitBreakerProto.Level.METHOD && rule1.getLevel() == rule2.getLevel()) { + int methodResult = CompareUtils.compareSingleValue(destMethod1, destMethod2); + if (methodResult != 0) { + return methodResult; + } + } + // 2. compare source service + String srcNamespace1 = ruleMatcher1.getSource().getNamespace(); + String srcService1 = ruleMatcher1.getSource().getService(); + String srcNamespace2 = ruleMatcher2.getSource().getNamespace(); + String srcService2 = ruleMatcher2.getSource().getService(); + return CompareUtils.compareService(srcNamespace1, srcService1, srcNamespace2, srcService2); + } + }); + return outRules; + } } diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/FaultDetectRuleDictionary.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/FaultDetectRuleDictionary.java index 20ac97c15..f673ff9ee 100644 --- a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/FaultDetectRuleDictionary.java +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/FaultDetectRuleDictionary.java @@ -17,92 +17,93 @@ package com.tencent.polaris.plugins.circuitbreaker.composite; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource; import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceRule; import com.tencent.polaris.api.utils.CollectionUtils; -import com.tencent.polaris.plugins.circuitbreaker.composite.utils.MatchUtils; +import com.tencent.polaris.api.utils.CompareUtils; import com.tencent.polaris.specification.api.v1.fault.tolerance.FaultDetectorProto; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class FaultDetectRuleDictionary { - // key is target service, value is list FaultDetectRule - private final Map> serviceRules = new ConcurrentHashMap<>(); + // key is target service, value is list FaultDetectRule + private final Map> serviceRules = new ConcurrentHashMap<>(); - private final Object updateLock = new Object(); + private final Object updateLock = new Object(); - public List lookupFaultDetectRule(Resource resource) { - ServiceKey targetService = resource.getService(); - return serviceRules.get(targetService); - } + public List lookupFaultDetectRule(Resource resource) { + ServiceKey targetService = resource.getService(); + return serviceRules.get(targetService); + } - /** - * rule on server has been changed, clear all caches to make it pull again - * @param svcKey target service - */ - public void onFaultDetectRuleChanged(ServiceKey svcKey, FaultDetectorProto.FaultDetector faultDetector) { - synchronized (updateLock) { - putServiceRule(svcKey, faultDetector); - } - } + /** + * rule on server has been changed, clear all caches to make it pull again + * + * @param svcKey target service + */ + public void onFaultDetectRuleChanged(ServiceKey svcKey, FaultDetectorProto.FaultDetector faultDetector) { + synchronized (updateLock) { + putServiceRule(svcKey, faultDetector); + } + } - void onFaultDetectRuleDeleted(ServiceKey svcKey) { - synchronized (updateLock) { - serviceRules.remove(svcKey); - } - } + void onFaultDetectRuleDeleted(ServiceKey svcKey) { + synchronized (updateLock) { + serviceRules.remove(svcKey); + } + } - public void putServiceRule(ServiceKey serviceKey, ServiceRule serviceRule) { - synchronized (updateLock) { - if (null == serviceRule) { - serviceRules.remove(serviceKey); - return; - } - putServiceRule(serviceKey, (FaultDetectorProto.FaultDetector) serviceRule.getRule()); - } - } + public void putServiceRule(ServiceKey serviceKey, ServiceRule serviceRule) { + synchronized (updateLock) { + if (null == serviceRule) { + serviceRules.remove(serviceKey); + return; + } + putServiceRule(serviceKey, (FaultDetectorProto.FaultDetector) serviceRule.getRule()); + } + } - public void putServiceRule(ServiceKey serviceKey, FaultDetectorProto.FaultDetector faultDetector) { - if (null == faultDetector) { - serviceRules.remove(serviceKey); - return; - } - List rules = faultDetector.getRulesList(); - if (CollectionUtils.isNotEmpty(rules)) { - rules = sortFaultDetectRules(rules); - } - serviceRules.put(serviceKey, rules); - } + public void putServiceRule(ServiceKey serviceKey, FaultDetectorProto.FaultDetector faultDetector) { + if (null == faultDetector) { + serviceRules.remove(serviceKey); + return; + } + List rules = faultDetector.getRulesList(); + if (CollectionUtils.isNotEmpty(rules)) { + rules = sortFaultDetectRules(rules); + } + serviceRules.put(serviceKey, rules); + } - private static List sortFaultDetectRules(List rules) { - List outRules = new ArrayList<>(rules); - outRules.sort(new Comparator() { - @Override - public int compare(FaultDetectorProto.FaultDetectRule rule1, FaultDetectorProto.FaultDetectRule rule2) { - // 1. compare destination service - FaultDetectorProto.FaultDetectRule.DestinationService targetService1 = rule1.getTargetService(); - String destNamespace1 = targetService1.getNamespace(); - String destService1 = targetService1.getService(); - String destMethod1 = targetService1.getMethod().getValue().getValue(); + private static List sortFaultDetectRules(List rules) { + List outRules = new ArrayList<>(rules); + outRules.sort(new Comparator() { + @Override + public int compare(FaultDetectorProto.FaultDetectRule rule1, FaultDetectorProto.FaultDetectRule rule2) { + // 1. compare destination service + FaultDetectorProto.FaultDetectRule.DestinationService targetService1 = rule1.getTargetService(); + String destNamespace1 = targetService1.getNamespace(); + String destService1 = targetService1.getService(); + String destMethod1 = targetService1.getMethod().getValue().getValue(); - FaultDetectorProto.FaultDetectRule.DestinationService targetService2 = rule2.getTargetService(); - String destNamespace2 = targetService2.getNamespace(); - String destService2 = targetService2.getService(); - String destMethod2 = targetService2.getMethod().getValue().getValue(); + FaultDetectorProto.FaultDetectRule.DestinationService targetService2 = rule2.getTargetService(); + String destNamespace2 = targetService2.getNamespace(); + String destService2 = targetService2.getService(); + String destMethod2 = targetService2.getMethod().getValue().getValue(); - int svcResult = MatchUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); - if (svcResult != 0) { - return svcResult; - } - return MatchUtils.compareSingleValue(destMethod1, destMethod2); - } - }); - return outRules; - } + int svcResult = CompareUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); + if (svcResult != 0) { + return svcResult; + } + return CompareUtils.compareSingleValue(destMethod1, destMethod2); + } + }); + return outRules; + } } diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/ResourceHealthChecker.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/ResourceHealthChecker.java index ab7f7fc1d..025705e7e 100644 --- a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/ResourceHealthChecker.java +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/ResourceHealthChecker.java @@ -17,20 +17,6 @@ package com.tencent.polaris.plugins.circuitbreaker.composite; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.regex.Pattern; - import com.tencent.polaris.api.plugin.circuitbreaker.ResourceStat; import com.tencent.polaris.api.plugin.circuitbreaker.entity.InstanceResource; import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource; @@ -51,247 +37,254 @@ import com.tencent.polaris.specification.api.v1.fault.tolerance.FaultDetectorProto.FaultDetectRule.Protocol; import org.slf4j.Logger; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.regex.Pattern; + import static com.tencent.polaris.logging.LoggingConsts.LOGGING_HEALTHCHECK_EVENT; public class ResourceHealthChecker { - private static final Logger HC_EVENT_LOG = LoggerFactory.getLogger(LOGGING_HEALTHCHECK_EVENT); - - private static final Logger LOG = LoggerFactory.getLogger(ResourceHealthChecker.class); - - private static final Object PLACE_HOLDER_RESOURCE = new Object(); - - private static final int DEFAULT_CHECK_INTERVAL = 30; - - private final ScheduledExecutorService checkScheduler; - - private final AtomicBoolean started = new AtomicBoolean(false); - - private final AtomicBoolean stopped = new AtomicBoolean(false); - - private final Map healthCheckers; - - private final PolarisCircuitBreaker polarisCircuitBreaker; - - private ScheduledFuture future; - - private final FaultDetectRule faultDetectRule; - - private final HealthCheckInstanceProvider healthCheckInstanceProvider; - - private final AtomicLong lastCheckTimeMilli; - - private final Function regexToPattern; - - private final Map resources = new ConcurrentHashMap<>(); - - public ResourceHealthChecker(FaultDetectRule faultDetectRule, - HealthCheckInstanceProvider healthCheckInstanceProvider, PolarisCircuitBreaker polarisCircuitBreaker) { - this.checkScheduler = polarisCircuitBreaker.getHealthCheckExecutors(); - this.healthCheckers = polarisCircuitBreaker.getHealthCheckers(); - this.polarisCircuitBreaker = polarisCircuitBreaker; - this.faultDetectRule = faultDetectRule; - this.healthCheckInstanceProvider = healthCheckInstanceProvider; - lastCheckTimeMilli = new AtomicLong(System.currentTimeMillis()); - if (null != polarisCircuitBreaker.getExtensions()) { - this.regexToPattern = polarisCircuitBreaker.getExtensions().getFlowCache()::loadOrStoreCompiledRegex; - } else { - this.regexToPattern = Pattern::compile; - } - } - - private Instance createDefaultInstance(String host, int port) { - DefaultInstance instance = new DefaultInstance(); - instance.setHost(host); - instance.setPort(port); - return instance; - } - - private Runnable createCheckTask() { - return () -> { - if (stopped.get()) { - return; - } - FaultDetectRule faultDetectRule = getFaultDetectRule(); - int interval = DEFAULT_CHECK_INTERVAL; - if (faultDetectRule.getInterval() > 0) { - interval = faultDetectRule.getInterval(); - } - if (System.currentTimeMillis() - lastCheckTimeMilli.get() >= interval) { - try { - checkResource(faultDetectRule); - } - finally { - lastCheckTimeMilli.set(System.currentTimeMillis()); - } - } - - }; - } - - private void checkResource(FaultDetectRule faultDetectRule) { - Map instances = healthCheckInstanceProvider.getInstances(); - if (CollectionUtils.isEmpty(instances) || CollectionUtils.isEmpty(resources)) { - return; - } - int port = faultDetectRule.getPort(); - Protocol protocol = faultDetectRule.getProtocol(); - if (port > 0) { - Set hosts = new HashSet<>(); - for (Map.Entry entry : instances.entrySet()) { - Node instance = entry.getKey(); - if (!hosts.contains(instance.getHost())) { - hosts.add(instance.getHost()); - boolean success = doCheck(createDefaultInstance(instance.getHost(), port), protocol, faultDetectRule); - entry.getValue().checkSuccess.set(success); - } - } - } - else { - for (Map.Entry entry : instances.entrySet()) { - Protocol currentProtocol = entry.getValue().getProtocol(); - if (currentProtocol == Protocol.UNKNOWN || protocol == currentProtocol) { - InstanceResource instance = entry.getValue().getInstanceResource(); - boolean success = doCheck( - createDefaultInstance(instance.getHost(), instance.getPort()), protocol, faultDetectRule); - entry.getValue().checkSuccess.set(success); - } - } - } - } - - public void start() { - if (started.compareAndSet(false, true)) { - Runnable checkTask = createCheckTask(); - FaultDetectRule faultDetectRule = getFaultDetectRule(); - HC_EVENT_LOG.info("schedule health check task: protocol {}, interval {}, rule {}", faultDetectRule.getProtocol(), - faultDetectRule.getInterval(), faultDetectRule.getName()); - this.future = checkScheduler - .scheduleWithFixedDelay(checkTask, DEFAULT_CHECK_INTERVAL, DEFAULT_CHECK_INTERVAL, TimeUnit.SECONDS); - } - } - - private boolean matchInstanceToResource(Instance instance, Resource resource) { - if (resource.getLevel() != CircuitBreakerProto.Level.INSTANCE) { - return true; - } - InstanceResource instanceResource = (InstanceResource) resource; - return StringUtils.equals(instance.getHost(), instanceResource.getHost()) && instance.getPort() == instanceResource.getPort(); - } - - private boolean doCheck(Instance instance, Protocol protocol, FaultDetectRule faultDetectRule) { - HealthChecker healthChecker = healthCheckers.get(protocol.name().toLowerCase()); - if (null == healthChecker) { - LOG.info("plugin not found, skip health check for instance {}:{}, protocol {}", - instance.getHost(), instance.getPort(), protocol); - return false; - } - DetectResult detectResult = healthChecker.detectInstance(instance, faultDetectRule); - HC_EVENT_LOG.info("health check for instance {}:{}, protocol {}, result: code {}, delay {}ms, status {}, rule {}", - instance.getHost(), instance.getPort(), protocol, detectResult.getStatusCode(), - detectResult.getDelay(), detectResult.getRetStatus(), faultDetectRule.getName()); - Set copiedResources = new HashSet<>(resources.keySet()); - Set reportedResources = new HashSet<>(); - for (Resource resource : copiedResources) { - if (!matchInstanceToResource(instance, resource)) { - continue; - } - Resource actualResource = polarisCircuitBreaker.getActualResource(resource); - if (reportedResources.contains(actualResource)) { - continue; - } - reportedResources.add(actualResource); - ResourceStat resourceStat = new ResourceStat( - actualResource, detectResult.getStatusCode(), detectResult.getDelay(), detectResult.getRetStatus()); - HC_EVENT_LOG.info("report health check to resource {}, status code {}, delay {}", actualResource, - detectResult.getStatusCode(), detectResult.getDelay()); - polarisCircuitBreaker.doReport(resourceStat, false); - } - return detectResult.getRetStatus() == RetStatus.RetSuccess; - } - - public void stop() { - HC_EVENT_LOG.info("health checker has stopped, rule {}", faultDetectRule.getName()); - stopped.set(true); - if (null != future) { - future.cancel(true); - } - } - - public FaultDetectRule getFaultDetectRule() { - return faultDetectRule; - } - - public static class ProtocolInstance { - - final Protocol protocol; - - final InstanceResource instanceResource; - - final AtomicLong lastReportMilli = new AtomicLong(0); - - final AtomicBoolean checkSuccess = new AtomicBoolean(true); - - ProtocolInstance( - Protocol protocol, InstanceResource instanceResource) { - this.protocol = protocol; - this.instanceResource = instanceResource; - lastReportMilli.set(System.currentTimeMillis()); - } - - Protocol getProtocol() { - return protocol; - } - - InstanceResource getInstanceResource() { - return instanceResource; - } - - public long getLastReportMilli() { - return lastReportMilli.get(); - } - - void doReport() { - lastReportMilli.set(System.currentTimeMillis()); - } - - boolean isCheckSuccess() { - return checkSuccess.get(); - } - } - - public boolean matchResource(Resource resource) { - FaultDetectRule faultDetectRule = getFaultDetectRule(); - FaultDetectorProto.FaultDetectRule.DestinationService targetService = faultDetectRule.getTargetService(); - if (!MatchUtils.matchService(resource.getService(), targetService.getNamespace(), targetService.getService())) { - return false; - } - if (resource.getLevel() == CircuitBreakerProto.Level.METHOD) { - if (!targetService.hasMethod() || StringUtils.isBlank(targetService.getMethod().getValue().getValue())) { - return false; - } - return MatchUtils.matchMethod(resource, targetService.getMethod(), regexToPattern); - } - else { - // only match empty method rules - return RuleUtils.isMatchAllValue(targetService.getMethod()); - } - } - - public void addResource(Resource resource) { - if (null == resources.putIfAbsent(resource, PLACE_HOLDER_RESOURCE)) { - HC_EVENT_LOG.info("add fault detect resource {}, rule {}", resource, faultDetectRule.getName()); - } - } - - public void removeResource(Resource resource) { - if (null != resources.remove(resource)) { - HC_EVENT_LOG.info("remove fault detect resource {}, rule {}", resource, faultDetectRule.getName()); - - } - } - - public Collection getResources() { - return Collections.unmodifiableCollection(resources.keySet()); - } + private static final Logger HC_EVENT_LOG = LoggerFactory.getLogger(LOGGING_HEALTHCHECK_EVENT); + + private static final Logger LOG = LoggerFactory.getLogger(ResourceHealthChecker.class); + + private static final Object PLACE_HOLDER_RESOURCE = new Object(); + + private static final int DEFAULT_CHECK_INTERVAL = 30; + + private final ScheduledExecutorService checkScheduler; + + private final AtomicBoolean started = new AtomicBoolean(false); + + private final AtomicBoolean stopped = new AtomicBoolean(false); + + private final Map healthCheckers; + + private final PolarisCircuitBreaker polarisCircuitBreaker; + + private ScheduledFuture future; + + private final FaultDetectRule faultDetectRule; + + private final HealthCheckInstanceProvider healthCheckInstanceProvider; + + private final AtomicLong lastCheckTimeMilli; + + private final Function regexToPattern; + + private final Map resources = new ConcurrentHashMap<>(); + + public ResourceHealthChecker(FaultDetectRule faultDetectRule, + HealthCheckInstanceProvider healthCheckInstanceProvider, PolarisCircuitBreaker polarisCircuitBreaker) { + this.checkScheduler = polarisCircuitBreaker.getHealthCheckExecutors(); + this.healthCheckers = polarisCircuitBreaker.getHealthCheckers(); + this.polarisCircuitBreaker = polarisCircuitBreaker; + this.faultDetectRule = faultDetectRule; + this.healthCheckInstanceProvider = healthCheckInstanceProvider; + lastCheckTimeMilli = new AtomicLong(System.currentTimeMillis()); + if (null != polarisCircuitBreaker.getExtensions()) { + this.regexToPattern = polarisCircuitBreaker.getExtensions().getFlowCache()::loadOrStoreCompiledRegex; + } else { + this.regexToPattern = Pattern::compile; + } + } + + private Instance createDefaultInstance(String host, int port) { + DefaultInstance instance = new DefaultInstance(); + instance.setHost(host); + instance.setPort(port); + return instance; + } + + private Runnable createCheckTask() { + return () -> { + if (stopped.get()) { + return; + } + FaultDetectRule faultDetectRule = getFaultDetectRule(); + int interval = DEFAULT_CHECK_INTERVAL; + if (faultDetectRule.getInterval() > 0) { + interval = faultDetectRule.getInterval(); + } + if (System.currentTimeMillis() - lastCheckTimeMilli.get() >= interval) { + try { + checkResource(faultDetectRule); + } finally { + lastCheckTimeMilli.set(System.currentTimeMillis()); + } + } + + }; + } + + private void checkResource(FaultDetectRule faultDetectRule) { + Map instances = healthCheckInstanceProvider.getInstances(); + if (CollectionUtils.isEmpty(instances) || CollectionUtils.isEmpty(resources)) { + return; + } + int port = faultDetectRule.getPort(); + Protocol protocol = faultDetectRule.getProtocol(); + if (port > 0) { + Set hosts = new HashSet<>(); + for (Map.Entry entry : instances.entrySet()) { + Node instance = entry.getKey(); + if (!hosts.contains(instance.getHost())) { + hosts.add(instance.getHost()); + boolean success = doCheck(createDefaultInstance(instance.getHost(), port), protocol, faultDetectRule); + entry.getValue().checkSuccess.set(success); + } + } + } else { + for (Map.Entry entry : instances.entrySet()) { + Protocol currentProtocol = entry.getValue().getProtocol(); + if (currentProtocol == Protocol.UNKNOWN || protocol == currentProtocol) { + InstanceResource instance = entry.getValue().getInstanceResource(); + boolean success = doCheck( + createDefaultInstance(instance.getHost(), instance.getPort()), protocol, faultDetectRule); + entry.getValue().checkSuccess.set(success); + } + } + } + } + + public void start() { + if (started.compareAndSet(false, true)) { + Runnable checkTask = createCheckTask(); + FaultDetectRule faultDetectRule = getFaultDetectRule(); + HC_EVENT_LOG.info("schedule health check task: protocol {}, interval {}, rule {}", faultDetectRule.getProtocol(), + faultDetectRule.getInterval(), faultDetectRule.getName()); + this.future = checkScheduler + .scheduleWithFixedDelay(checkTask, DEFAULT_CHECK_INTERVAL, DEFAULT_CHECK_INTERVAL, TimeUnit.SECONDS); + } + } + + private boolean matchInstanceToResource(Instance instance, Resource resource) { + if (resource.getLevel() != CircuitBreakerProto.Level.INSTANCE) { + return true; + } + InstanceResource instanceResource = (InstanceResource) resource; + return StringUtils.equals(instance.getHost(), instanceResource.getHost()) && instance.getPort() == instanceResource.getPort(); + } + + private boolean doCheck(Instance instance, Protocol protocol, FaultDetectRule faultDetectRule) { + HealthChecker healthChecker = healthCheckers.get(protocol.name().toLowerCase()); + if (null == healthChecker) { + LOG.info("plugin not found, skip health check for instance {}:{}, protocol {}", + instance.getHost(), instance.getPort(), protocol); + return false; + } + DetectResult detectResult = healthChecker.detectInstance(instance, faultDetectRule); + HC_EVENT_LOG.info("health check for instance {}:{}, protocol {}, result: code {}, delay {}ms, status {}, rule {}", + instance.getHost(), instance.getPort(), protocol, detectResult.getStatusCode(), + detectResult.getDelay(), detectResult.getRetStatus(), faultDetectRule.getName()); + Set copiedResources = new HashSet<>(resources.keySet()); + Set reportedResources = new HashSet<>(); + for (Resource resource : copiedResources) { + if (!matchInstanceToResource(instance, resource)) { + continue; + } + Resource actualResource = polarisCircuitBreaker.getActualResource(resource); + if (reportedResources.contains(actualResource)) { + continue; + } + reportedResources.add(actualResource); + ResourceStat resourceStat = new ResourceStat( + actualResource, detectResult.getStatusCode(), detectResult.getDelay(), detectResult.getRetStatus()); + HC_EVENT_LOG.info("report health check to resource {}, status code {}, delay {}", actualResource, + detectResult.getStatusCode(), detectResult.getDelay()); + polarisCircuitBreaker.doReport(resourceStat, false); + } + return detectResult.getRetStatus() == RetStatus.RetSuccess; + } + + public void stop() { + HC_EVENT_LOG.info("health checker has stopped, rule {}", faultDetectRule.getName()); + stopped.set(true); + if (null != future) { + future.cancel(true); + } + } + + public FaultDetectRule getFaultDetectRule() { + return faultDetectRule; + } + + public static class ProtocolInstance { + + final Protocol protocol; + + final InstanceResource instanceResource; + + final AtomicLong lastReportMilli = new AtomicLong(0); + + final AtomicBoolean checkSuccess = new AtomicBoolean(true); + + ProtocolInstance( + Protocol protocol, InstanceResource instanceResource) { + this.protocol = protocol; + this.instanceResource = instanceResource; + lastReportMilli.set(System.currentTimeMillis()); + } + + Protocol getProtocol() { + return protocol; + } + + InstanceResource getInstanceResource() { + return instanceResource; + } + + public long getLastReportMilli() { + return lastReportMilli.get(); + } + + void doReport() { + lastReportMilli.set(System.currentTimeMillis()); + } + + boolean isCheckSuccess() { + return checkSuccess.get(); + } + } + + public boolean matchResource(Resource resource) { + FaultDetectRule faultDetectRule = getFaultDetectRule(); + FaultDetectorProto.FaultDetectRule.DestinationService targetService = faultDetectRule.getTargetService(); + if (!RuleUtils.matchService(resource.getService(), targetService.getNamespace(), targetService.getService())) { + return false; + } + if (resource.getLevel() == CircuitBreakerProto.Level.METHOD) { + if (!targetService.hasMethod() || StringUtils.isBlank(targetService.getMethod().getValue().getValue())) { + return false; + } + return MatchUtils.matchMethod(resource, targetService.getMethod(), regexToPattern); + } else { + // only match empty method rules + return RuleUtils.isMatchAllValue(targetService.getMethod()); + } + } + + public void addResource(Resource resource) { + if (null == resources.putIfAbsent(resource, PLACE_HOLDER_RESOURCE)) { + HC_EVENT_LOG.info("add fault detect resource {}, rule {}", resource, faultDetectRule.getName()); + } + } + + public void removeResource(Resource resource) { + if (null != resources.remove(resource)) { + HC_EVENT_LOG.info("remove fault detect resource {}, rule {}", resource, faultDetectRule.getName()); + + } + } + + public Collection getResources() { + return Collections.unmodifiableCollection(resources.keySet()); + } } diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/utils/MatchUtils.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/utils/MatchUtils.java index 9f60a73cb..b896d34e9 100644 --- a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/utils/MatchUtils.java +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker-composite/src/main/java/com/tencent/polaris/plugins/circuitbreaker/composite/utils/MatchUtils.java @@ -17,72 +17,23 @@ package com.tencent.polaris.plugins.circuitbreaker.composite.utils; -import java.util.function.Function; -import java.util.regex.Pattern; - import com.tencent.polaris.api.plugin.circuitbreaker.entity.MethodResource; import com.tencent.polaris.api.plugin.circuitbreaker.entity.Resource; -import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.utils.RuleUtils; -import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto.Level; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString; -public class MatchUtils { - - public static boolean matchService(ServiceKey serviceKey, String namespace, String service) { - String inputNamespace = ""; - String inputService = ""; - if (null != serviceKey) { - inputNamespace = serviceKey.getNamespace(); - inputService = serviceKey.getService(); - } - if (StringUtils.isNotBlank(namespace) && !StringUtils.equals(namespace, RuleUtils.MATCH_ALL) && !StringUtils - .equals(inputNamespace, namespace)) { - return false; - } - if (StringUtils.isNotBlank(service) && !StringUtils.equals(service, RuleUtils.MATCH_ALL) && !StringUtils - .equals(inputService, service)) { - return false; - } - return true; - } - - public static boolean matchMethod(Resource resource, MatchString matchString, - Function regexToPattern) { - if (resource.getLevel() != Level.METHOD) { - return true; - } - String method = ((MethodResource) resource).getMethod(); - return RuleUtils.matchStringValue(matchString, method, regexToPattern); - } - - public static boolean isWildcardMatcherSingle(String name) { - return name.equals(RuleUtils.MATCH_ALL) || StringUtils.isBlank(name); - } +import java.util.function.Function; +import java.util.regex.Pattern; - public static int compareSingleValue(String value1, String value2) { - boolean serviceWildcard1 = isWildcardMatcherSingle(value1); - boolean serviceWildcard2 = isWildcardMatcherSingle(value2); - if (serviceWildcard1 && serviceWildcard2) { - return 0; - } - if (serviceWildcard1) { - // 1 before 2 - return 1; - } - if (serviceWildcard2) { - // 1 before 2 - return -1; - } - return value1.compareTo(value2); - } +public class MatchUtils { - public static int compareService(String namespace1, String service1, String namespace2, String service2) { - int nsResult = compareSingleValue(namespace1, namespace2); - if (nsResult != 0) { - return nsResult; - } - return compareSingleValue(service1, service2); - } + public static boolean matchMethod(Resource resource, MatchString matchString, + Function regexToPattern) { + if (resource.getLevel() != Level.METHOD) { + return true; + } + String method = ((MethodResource) resource).getMethod(); + return RuleUtils.matchStringValue(matchString, method, regexToPattern); + } } diff --git a/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java b/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java index 8cf4b970e..6c30b1408 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java +++ b/polaris-plugins/polaris-plugins-connector/connector-composite/src/main/java/com/tencent/polaris/plugins/connector/composite/CompositeServiceUpdateTask.java @@ -117,7 +117,10 @@ public void execute() { // TODO 全部规则实现完后改成StringUtils.equals(mainConnectorType, SERVER_CONNECTOR_CONSUL) if (ifMainConnectorTypeSet && isServiceUpdateTaskExecuted && (StringUtils.equals(mainConnectorType, SERVER_CONNECTOR_GRPC) - || (serviceEventKey.getEventType().equals(EventType.INSTANCE) || serviceEventKey.getEventType().equals(EventType.SERVICE) || serviceEventKey.getEventType().equals(EventType.ROUTING)))) { + || (serviceEventKey.getEventType().equals(EventType.INSTANCE) + || serviceEventKey.getEventType().equals(EventType.SERVICE) + || serviceEventKey.getEventType().equals(EventType.ROUTING) + || serviceEventKey.getEventType().equals(EventType.NEARBY_ROUTE_RULE)))) { return; } boolean svcDeleted = this.notifyServerEvent( diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java index e514b6ff3..d7b74bfce 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/ConsulAPIConnector.java @@ -48,7 +48,8 @@ import com.tencent.polaris.plugins.connector.consul.service.ConsulService; import com.tencent.polaris.plugins.connector.consul.service.InstanceService; import com.tencent.polaris.plugins.connector.consul.service.ServiceService; -import com.tencent.polaris.plugins.connector.consul.service.router.RouterRuleService; +import com.tencent.polaris.plugins.connector.consul.service.router.NearByRouteRuleService; +import com.tencent.polaris.plugins.connector.consul.service.router.RoutingService; import org.slf4j.Logger; import java.util.*; @@ -222,7 +223,8 @@ private void initActually(InitContext ctx, ServerConnectorConfig connectorConfig // init consul service consulServiceMap.put(ServiceEventKey.EventType.INSTANCE, new InstanceService(consulClient, consulRawClient, consulContext, "consul-instance", mapper)); consulServiceMap.put(ServiceEventKey.EventType.SERVICE, new ServiceService(consulClient, consulRawClient, consulContext, "consul-service", mapper)); - consulServiceMap.put(ServiceEventKey.EventType.ROUTING, new RouterRuleService(consulClient, consulRawClient, consulContext, "consul-router-rule", mapper)); + consulServiceMap.put(ServiceEventKey.EventType.ROUTING, new RoutingService(consulClient, consulRawClient, consulContext, "consul-routing", mapper)); + consulServiceMap.put(ServiceEventKey.EventType.NEARBY_ROUTE_RULE, new NearByRouteRuleService(consulClient, consulRawClient, consulContext, "consul-nearby-route-rule", mapper)); initialized = true; } diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/InstanceService.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/InstanceService.java index 68d806f18..aacb9078a 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/InstanceService.java +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/InstanceService.java @@ -39,6 +39,7 @@ import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.metadata.core.constant.TsfMetadataConstants; import com.tencent.polaris.plugins.connector.common.ServiceUpdateTask; import com.tencent.polaris.plugins.connector.consul.ConsulContext; import com.tencent.polaris.specification.api.v1.model.ModelProto; @@ -133,13 +134,20 @@ public void sendRequest(ServiceUpdateTask serviceUpdateTask) { LOG.info("Instance with name {} host {} port {} doesn't have id.", serviceId , findHost(healthService), healthService.getService().getPort()); } - // set location - instanceBuilder.setLocation(ModelProto.Location.newBuilder().build()); // set metadata Map metadata = getMetadata(healthService); if (CollectionUtils.isNotEmpty(metadata)) { instanceBuilder.putAllMetadata(metadata); } + // set location + ModelProto.Location.Builder locationBuilder = ModelProto.Location.newBuilder(); + if (metadata.containsKey(TsfMetadataConstants.TSF_ZONE)) { + locationBuilder.setZone(StringValue.of(metadata.get(TsfMetadataConstants.TSF_ZONE))); + } + if (metadata.containsKey(TsfMetadataConstants.TSF_REGION)) { + locationBuilder.setRegion(StringValue.of(metadata.get(TsfMetadataConstants.TSF_REGION))); + } + instanceBuilder.setLocation(locationBuilder.build()); instanceList.add(instanceBuilder.build()); } } diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/NearByRouteRuleService.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/NearByRouteRuleService.java new file mode 100644 index 000000000..1246357c1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/NearByRouteRuleService.java @@ -0,0 +1,264 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.plugins.connector.consul.service.router; + +import com.ecwid.consul.SingleUrlParameters; +import com.ecwid.consul.UrlParameters; +import com.ecwid.consul.json.GsonFactory; +import com.ecwid.consul.transport.HttpResponse; +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.ConsulRawClient; +import com.ecwid.consul.v1.QueryParams; +import com.ecwid.consul.v1.kv.model.GetValue; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; +import com.google.gson.reflect.TypeToken; +import com.google.protobuf.Any; +import com.google.protobuf.StringValue; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.exception.ServerErrorResponseException; +import com.tencent.polaris.api.plugin.server.ServerEvent; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.plugins.connector.common.ServiceUpdateTask; +import com.tencent.polaris.plugins.connector.consul.ConsulContext; +import com.tencent.polaris.plugins.connector.consul.service.ConsulService; +import com.tencent.polaris.plugins.connector.consul.service.router.entity.RouteAffinity; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import com.tencent.polaris.specification.api.v1.service.manage.ServiceProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; +import org.slf4j.Logger; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.representer.Representer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import static com.tencent.polaris.api.config.plugin.DefaultPlugins.SERVER_CONNECTOR_CONSUL; + +/** + * @author Haotian Zhang + */ +public class NearByRouteRuleService extends ConsulService { + + private static final Logger LOG = LoggerFactory.getLogger(NearByRouteRuleService.class); + + private final Map affinityConsulIndexMap = new ConcurrentHashMap<>(); + + public NearByRouteRuleService(ConsulClient consulClient, ConsulRawClient consulRawClient, ConsulContext consulContext, + String threadName, ObjectMapper mapper) { + super(consulClient, consulRawClient, consulContext, threadName, mapper); + } + + @Override + public void sendRequest(ServiceUpdateTask serviceUpdateTask) { + String namespace = serviceUpdateTask.getServiceEventKey().getNamespace(); + String service = serviceUpdateTask.getServiceEventKey().getService(); + String routeAffinityKey = String.format("/v1/kv/affinity/%s/data", namespace); + // 带等待时间发起对Consul的KV请求 + LOG.trace("tsf route affinity, consul kv namespace, getKey: {}", routeAffinityKey); + UrlParameters nsTypeParam = new SingleUrlParameters("nsType", "DEF_AND_GLOBAL"); + UrlParameters tokenParam = new SingleUrlParameters("token", consulContext.getAclToken()); + NearByRouteRuleKey nearByRouteRuleKey = new NearByRouteRuleKey(); + nearByRouteRuleKey.setNamespace(namespace); + nearByRouteRuleKey.setService(service); + Long currentIndex = getRouterRuleConsulIndex(nearByRouteRuleKey); + QueryParams queryParams = new QueryParams(consulContext.getWaitTime(), currentIndex); + int code = ServerCodes.DATA_NO_CHANGE; + try { + LOG.debug("Begin get affinity rules of {}:{} sync", namespace, service); + HttpResponse rawResponse = consulRawClient.makeGetRequest(routeAffinityKey, tokenParam, + nsTypeParam, queryParams); + if (rawResponse != null) { + if (LOG.isDebugEnabled()) { + String responseStr = "RawResponse{" + + "statusCode=" + rawResponse.getStatusCode() + + ", statusMessage='" + rawResponse.getStatusMessage() + '\'' + + ", content='" + rawResponse.getContent() + '\'' + + ", consulIndex=" + rawResponse.getConsulIndex() + '\'' + + ", consulKnownLeader=" + rawResponse.isConsulKnownLeader() + '\'' + + ", consulLastContact=" + rawResponse.getConsulLastContact() + + '}'; + LOG.debug("tsf route affinity, consul kv namespace, response: {}", responseStr); + } + + Long newIndex = rawResponse.getConsulIndex(); + // create service. + ServiceProto.Service.Builder newServiceBuilder = ServiceProto.Service.newBuilder(); + newServiceBuilder.setNamespace(StringValue.of(namespace)); + newServiceBuilder.setName(StringValue.of(service)); + newServiceBuilder.setRevision(StringValue.of(String.valueOf(newIndex))); + // create route rule list. + List routes = new ArrayList<>(); + // create discover response. + ResponseProto.DiscoverResponse.Builder newDiscoverResponseBuilder = ResponseProto.DiscoverResponse.newBuilder(); + newDiscoverResponseBuilder.setService(newServiceBuilder); + if (Objects.nonNull(newIndex)) { + if (!Objects.equals(currentIndex, newIndex)) { + code = ServerCodes.EXECUTE_SUCCESS; + if (rawResponse.getStatusCode() == 200) { + if (rawResponse.getContent() != null) { + LOG.info("new affinity route rule: {}", rawResponse.getContent()); + routes = parseResponse(rawResponse, namespace, service); + } + } else if (rawResponse.getStatusCode() == 404) { + LOG.info("empty route rule: {}", rawResponse.getContent()); + } + } else { + LOG.debug("[TSF Route Affinity] Consul data is not changed"); + } + } else { + LOG.warn("[TSF Route Affinity] Consul data is abnormal. {}", rawResponse); + } + if (CollectionUtils.isNotEmpty(routes)) { + newDiscoverResponseBuilder.addAllNearbyRouteRules(routes); + } + newDiscoverResponseBuilder.setCode(UInt32Value.of(code)); + ServerEvent serverEvent = new ServerEvent(serviceUpdateTask.getServiceEventKey(), newDiscoverResponseBuilder.build(), null, SERVER_CONNECTOR_CONSUL); + boolean svcDeleted = serviceUpdateTask.notifyServerEvent(serverEvent); + // 重写index + if (newIndex != null) { + setRouterRuleConsulIndex(nearByRouteRuleKey, currentIndex, newIndex); + } + if (!svcDeleted) { + serviceUpdateTask.addUpdateTaskSet(); + } + } + } catch (Throwable e) { + LOG.error("[TSF Route Rule] tsf route affinity rule load error. Will sleep for {} ms. Key path:{}", + consulContext.getConsulErrorSleep(), routeAffinityKey, e); + try { + Thread.sleep(consulContext.getConsulErrorSleep()); + } catch (Exception e1) { + LOG.error("error in sleep, msg: {}", e1.getMessage()); + } + PolarisException error = ServerErrorResponseException.build(ErrorCode.NETWORK_ERROR.getCode(), + "Get nearby route rule sync failed."); + ServerEvent serverEvent = new ServerEvent(serviceUpdateTask.getServiceEventKey(), null, error, SERVER_CONNECTOR_CONSUL); + serviceUpdateTask.notifyServerEvent(serverEvent); + } + } + + private List parseResponse(final HttpResponse response, String namespace, String service) { + List valueList = GsonFactory.getGson().fromJson(response.getContent(), + new TypeToken>() { + }.getType()); + // yaml -> json -> list + Representer representer = new Representer(new DumperOptions()); + representer.addClassTag(RouteAffinity.class, Tag.MAP); + representer.getPropertyUtils().setSkipMissingProperties(true); + Yaml yaml = new Yaml(representer); + ObjectMapper mapper = new ObjectMapper(); + // 配置 ObjectMapper在反序列化时,忽略目标对象没有的属性 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + List routeAffinityList = Lists.newArrayList(); + valueList.forEach(value -> { + try { + String routeJsonString = mapper + .writeValueAsString(yaml.load(value.getDecodedValue())); + RouteAffinity routeAffinity = yaml.loadAs(routeJsonString, RouteAffinity.class); + LOG.info("tsf route affinity, namespace:{}, affinity: {}", namespace, routeAffinity.getAffinity()); + routeAffinityList.add(routeAffinity); + } catch (Exception ex) { + LOG.error("tsf affinity rule load error.", ex); + throw new PolarisException(ErrorCode.INVALID_RESPONSE, "tsf affinity rule load error.", ex); + } + }); + + // list -> List + List routes = Lists.newArrayList(); + for (RouteAffinity routeAffinity : routeAffinityList) { + RoutingProto.RouteRule.Builder routeRuleBuilder = RoutingProto.RouteRule.newBuilder(); + routeRuleBuilder.setNamespace(routeAffinity.getNamespaceId()); + routeRuleBuilder.setEnable(routeAffinity.getAffinity()); + RoutingProto.NearbyRoutingConfig.Builder nearbyRoutingConfigBuilder = RoutingProto.NearbyRoutingConfig.newBuilder(); + nearbyRoutingConfigBuilder.setNamespace(namespace); + nearbyRoutingConfigBuilder.setService(service); + nearbyRoutingConfigBuilder.setMatchLevel(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE); + nearbyRoutingConfigBuilder.setMaxMatchLevel(RoutingProto.NearbyRoutingConfig.LocationLevel.ALL); + routeRuleBuilder.setRoutingConfig(Any.pack(nearbyRoutingConfigBuilder.build())); + routes.add(routeRuleBuilder.build()); + } + return routes; + } + + private Long getRouterRuleConsulIndex(NearByRouteRuleKey nearByRouteRuleKey) { + Long index = affinityConsulIndexMap.get(nearByRouteRuleKey); + if (index != null) { + return index; + } + setRouterRuleConsulIndex(nearByRouteRuleKey, null, -1L); + return -1L; + } + + private void setRouterRuleConsulIndex(NearByRouteRuleKey nearByRouteRuleKey, Long lastIndex, Long newIndex) { + LOG.debug("NearByRouteRuleKey: {}; lastIndex: {}; newIndex: {}", nearByRouteRuleKey, lastIndex, newIndex); + affinityConsulIndexMap.put(nearByRouteRuleKey, newIndex); + } + + static class NearByRouteRuleKey { + private String namespace = ""; + private String service = ""; + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + NearByRouteRuleKey that = (NearByRouteRuleKey) object; + return Objects.equals(getNamespace(), that.getNamespace()) && Objects.equals(getService(), that.getService()); + } + + @Override + public int hashCode() { + return Objects.hash(getNamespace(), getService()); + } + + @Override + public String toString() { + return "NearByRouteRuleKey{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + '}'; + } + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RouterRuleService.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RoutingService.java similarity index 98% rename from polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RouterRuleService.java rename to polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RoutingService.java index 03b5aa267..424192095 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RouterRuleService.java +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RoutingService.java @@ -67,14 +67,14 @@ /** * @author Haotian Zhang */ -public class RouterRuleService extends ConsulService { +public class RoutingService extends ConsulService { - private static final Logger LOG = LoggerFactory.getLogger(RouterRuleService.class); + private static final Logger LOG = LoggerFactory.getLogger(RoutingService.class); private final Map routerRuleConsulIndexMap = new ConcurrentHashMap<>(); - public RouterRuleService(ConsulClient consulClient, ConsulRawClient consulRawClient, ConsulContext consulContext, - String threadName, ObjectMapper mapper) { + public RoutingService(ConsulClient consulClient, ConsulRawClient consulRawClient, ConsulContext consulContext, + String threadName, ObjectMapper mapper) { super(consulClient, consulRawClient, consulContext, threadName, mapper); } @@ -83,6 +83,7 @@ public void sendRequest(ServiceUpdateTask serviceUpdateTask) { String namespace = serviceUpdateTask.getServiceEventKey().getNamespace(); String service = serviceUpdateTask.getServiceEventKey().getService(); String routeRuleKey = String.format("/v1/kv/route/%s/%s/data", namespace, service); + LOG.trace("tsf route rule, consul kv namespace, getKey: {}", routeRuleKey); UrlParameters nsTypeParam = new SingleUrlParameters("nsType", "DEF_AND_GLOBAL"); UrlParameters tokenParam = new SingleUrlParameters("token", consulContext.getAclToken()); UrlParameters recurseParam = new SingleUrlParameters("recurse"); diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteAffinity.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteAffinity.java new file mode 100644 index 000000000..cd96f631d --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteAffinity.java @@ -0,0 +1,39 @@ +package com.tencent.polaris.plugins.connector.consul.service.router.entity; + +import java.io.Serializable; + +/** + * 路由就近访问策略 + */ +public class RouteAffinity implements Serializable { + + private static final long serialVersionUID = -1879939014196760924L; + + private String namespaceId; + + private Boolean affinity; + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public Boolean getAffinity() { + return affinity; + } + + public void setAffinity(Boolean affinity) { + this.affinity = affinity; + } + + @Override + public String toString() { + return "RouteAffinity{" + + "namespaceId='" + namespaceId + '\'' + + ", affinity=" + affinity + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java index 1a396004c..4a82ac73d 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java @@ -235,6 +235,8 @@ public static DiscoverRequestType buildDiscoverRequestType( return DiscoverRequestType.FAULT_DETECTOR; case LANE_RULE: return DiscoverRequestType.LANE; + case NEARBY_ROUTE_RULE: + return DiscoverRequestType.NEARBY_ROUTE_RULE; default: return DiscoverRequestType.UNKNOWN; } @@ -257,6 +259,8 @@ public static DiscoverResponseType buildDiscoverResponseType( return DiscoverResponseType.FAULT_DETECTOR; case LANE_RULE: return DiscoverResponseType.LANE; + case NEARBY_ROUTE_RULE: + return DiscoverResponseType.NEARBY_ROUTE_RULE; default: return DiscoverResponseType.UNKNOWN; } @@ -278,6 +282,8 @@ public static EventType buildEventType(DiscoverResponseType responseType) { return EventType.FAULT_DETECTING; case LANE: return EventType.LANE_RULE; + case NEARBY_ROUTE_RULE: + return EventType.NEARBY_ROUTE_RULE; default: return EventType.UNKNOWN; } diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/NearByRouteRuleCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/NearByRouteRuleCacheHandler.java new file mode 100644 index 000000000..9ce3c6c2f --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/NearByRouteRuleCacheHandler.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.registry.AbstractCacheHandler; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.utils.CompareUtils; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import com.tencent.polaris.specification.api.v1.service.manage.ServiceProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class NearByRouteRuleCacheHandler extends AbstractCacheHandler { + + private static final Logger LOG = LoggerFactory.getLogger(NearByRouteRuleCacheHandler.class); + + @Override + protected String getRevision(ResponseProto.DiscoverResponse discoverResponse) { + ServiceProto.Service service = discoverResponse.getService(); + return service.getRevision().getValue(); + } + + @Override + public ServiceEventKey.EventType getTargetEventType() { + return ServiceEventKey.EventType.NEARBY_ROUTE_RULE; + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { + ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) newValue; + String revision = discoverResponse.getService().getRevision().getValue(); + + // 排序规则 + ResponseProto.DiscoverResponse.Builder newDiscoverResponseBuilder = ResponseProto.DiscoverResponse.newBuilder() + .mergeFrom(discoverResponse); + List unmodifiableList = discoverResponse.getNearbyRouteRulesList(); + List nearByRouteRuleList = sortNearbyRouteRules(unmodifiableList); + newDiscoverResponseBuilder.clearNearbyRouteRules(); + newDiscoverResponseBuilder.addAllNearbyRouteRules(nearByRouteRuleList); + + return new ServiceRuleByProto(newDiscoverResponseBuilder.build(), revision, isCacheLoaded, getTargetEventType()); + } + + private List sortNearbyRouteRules(List rules) { + List sorted = new ArrayList<>(rules); + // 数字越小,规则优先级越大 + sorted.sort(Comparator.comparingInt(RoutingProto.RouteRule::getPriority)); + sorted.sort((o1, o2) -> { + // 比较优先级,数字越小,规则优先级越大 + int priorityResult = o1.getPriority() - o2.getPriority(); + if (priorityResult != 0) { + return priorityResult; + } + + // 比较目标服务 + String destNamespace1 = ""; + String destService1 = ""; + try { + RoutingProto.NearbyRoutingConfig nearbyRoutingConfig = o1.getRoutingConfig().unpack(RoutingProto.NearbyRoutingConfig.class); + destNamespace1 = nearbyRoutingConfig.getNamespace(); + destService1 = nearbyRoutingConfig.getService(); + } catch (Exception e) { + LOG.warn("{} cannot be unpacked to an instance of RoutingProto.NearbyRoutingConfig", o1); + } + + String destNamespace2 = ""; + String destService2 = ""; + try { + RoutingProto.NearbyRoutingConfig nearbyRoutingConfig = o2.getRoutingConfig().unpack(RoutingProto.NearbyRoutingConfig.class); + destNamespace2 = nearbyRoutingConfig.getNamespace(); + destService2 = nearbyRoutingConfig.getService(); + } catch (Exception e) { + LOG.warn("{} cannot be unpacked to an instance of RoutingProto.NearbyRoutingConfig", o2); + } + int serviceKeyResult = CompareUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); + if (serviceKeyResult != 0) { + return serviceKeyResult; + } + + String ruleId1 = o1.getId(); + String ruleId2 = o1.getId(); + return CompareUtils.compareSingleValue(ruleId1, ruleId2); + }); + return sorted; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler index 68e393712..364cf1148 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler @@ -3,4 +3,5 @@ com.tencent.polaris.plugins.connector.grpc.codec.RoutingCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.CircuitBreakCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.ServicesCacheHandler com.tencent.polaris.plugins.connector.grpc.codec.FaultDetectCacheHandler -com.tencent.polaris.plugins.connector.grpc.codec.LaneRuleCacheHandler \ No newline at end of file +com.tencent.polaris.plugins.connector.grpc.codec.LaneRuleCacheHandler +com.tencent.polaris.plugins.connector.grpc.codec.NearByRouteRuleCacheHandler \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-router/pom.xml b/polaris-plugins/polaris-plugins-router/pom.xml index 2d2b0f9cf..6d47b619d 100644 --- a/polaris-plugins/polaris-plugins-router/pom.xml +++ b/polaris-plugins/polaris-plugins-router/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> polaris-plugins com.tencent.polaris @@ -26,6 +26,7 @@ router-metadata router-common router-lane + router-namespace diff --git a/polaris-plugins/polaris-plugins-router/router-namespace/pom.xml b/polaris-plugins/polaris-plugins-router/router-namespace/pom.xml new file mode 100644 index 000000000..b3ee7c65f --- /dev/null +++ b/polaris-plugins/polaris-plugins-router/router-namespace/pom.xml @@ -0,0 +1,42 @@ + + + + polaris-plugins-router + com.tencent.polaris + ${revision} + ../pom.xml + + 4.0.0 + + router-namespace + + Polaris Plugins Router Namespace + Polaris Plugins Router Namespace JAR + + + + com.tencent.polaris + router-common + ${project.version} + + + com.tencent.polaris + polaris-logging + ${project.version} + + + com.tencent.polaris + polaris-metadata + ${project.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-router/router-namespace/src/main/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouter.java b/polaris-plugins/polaris-plugins-router/router-namespace/src/main/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouter.java new file mode 100644 index 000000000..3f602fb26 --- /dev/null +++ b/polaris-plugins/polaris-plugins-router/router-namespace/src/main/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouter.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.plugins.router.namespace; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.rpc.NamespaceRouterFailoverType; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import org.slf4j.Logger; + +import java.util.*; + +/** + * 命名空间就近路由,优先返回同命名空间的实例节点。 + * + * @author Haotian Zhang + */ +public class NamespaceRouter extends AbstractServiceRouter { + + private static final Logger LOG = LoggerFactory.getLogger(NamespaceRouter.class); + + private static final String NAMESPACE_ROUTER_ENABLE = "internal-enable-router-namespace"; + + public static final String ROUTER_TYPE_NAMESPACE = "namespaceRoute"; + + public static final String ROUTER_ENABLED = "enabled"; + + /** + * 默认关闭,需要显示打开 + * + * @param routeInfo 路由参数 + * @param dstSvcInfo 被调服务 + * @return if enabled + */ + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + // 判断目标服务的服务元数据 + Map destSvcMetadata = Optional.ofNullable(dstSvcInfo.getMetadata()).orElse(Collections.emptyMap()); + if (Boolean.parseBoolean(destSvcMetadata.get(NAMESPACE_ROUTER_ENABLE))) { + return true; + } + + // 判断目标服务的路由请求 + if (routeInfo.getMetadataContainerGroup() != null && routeInfo.getMetadataContainerGroup().getCustomMetadataContainer() != null) { + String enabledStr = routeInfo.getMetadataContainerGroup().getCustomMetadataContainer().getRawMetadataMapValue(ROUTER_TYPE_NAMESPACE, ROUTER_ENABLED); + return StringUtils.isNotBlank(enabledStr) && Boolean.parseBoolean(enabledStr); + } + + return false; + } + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_NAMESPACE; + } + + + @Override + public Aspect getAspect() { + return Aspect.MIDDLE; + } + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances svcInstances) throws PolarisException { + List instances = new ArrayList<>(); + for (Instance instance : svcInstances.getInstances()) { + if (StringUtils.equals(routeInfo.getSourceService().getNamespace(), instance.getNamespace())) { + instances.add(instance); + } + } + if (CollectionUtils.isEmpty(instances)) { + LOG.debug("No same namespace instance from {} instances of {}:{}", svcInstances.getInstances().size(), + routeInfo.getSourceService().getNamespace(), routeInfo.getSourceService().getService()); + if (routeInfo.getNamespaceRouterFailoverType() == NamespaceRouterFailoverType.none) { + LOG.debug("Fail over to empty instance list of {}:{}", + routeInfo.getSourceService().getNamespace(), routeInfo.getSourceService().getService()); + } else { + LOG.debug("Fail over to original instance list of {}:{}", + routeInfo.getSourceService().getNamespace(), routeInfo.getSourceService().getService()); + instances.addAll(svcInstances.getInstances()); + } + } + return new RouteResult(instances, RouteResult.State.Next); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } +} diff --git a/polaris-plugins/polaris-plugins-router/router-namespace/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-router/router-namespace/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..400663782 --- /dev/null +++ b/polaris-plugins/polaris-plugins-router/router-namespace/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.namespace.NamespaceRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-router/router-namespace/src/test/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouterTest.java b/polaris-plugins/polaris-plugins-router/router-namespace/src/test/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouterTest.java new file mode 100644 index 000000000..3968d67ed --- /dev/null +++ b/polaris-plugins/polaris-plugins-router/router-namespace/src/test/java/com/tencent/polaris/plugins/router/namespace/NamespaceRouterTest.java @@ -0,0 +1,137 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.polaris.plugins.router.namespace; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.pojo.*; +import com.tencent.polaris.api.rpc.NamespaceRouterFailoverType; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link NamespaceRouterTest}. + * + * @author Haotian Zhang + */ +public class NamespaceRouterTest { + + public static String testNamespace1 = "testNamespace1"; + public static String testNamespace2 = "testNamespace2"; + + private final NamespaceRouter namespaceRouter = new NamespaceRouter(); + + @Test + public void test1() { + SourceService sourceService = new SourceService(); + sourceService.setNamespace(testNamespace1); + RouteInfo routeInfo = new RouteInfo(sourceService, null, null, null); + + List instances = new ArrayList<>(); + DefaultBaseInstance instance0 = new DefaultInstance(); + instance0.setNamespace(testNamespace1); + instance0.setHost("0.0.0.0"); + instances.add((Instance) instance0); + DefaultBaseInstance instance1 = new DefaultInstance(); + instance1.setNamespace(testNamespace1); + instance1.setHost("1.1.1.1"); + instances.add((Instance) instance1); + DefaultBaseInstance instance2 = new DefaultInstance(); + instance2.setNamespace(testNamespace2); + instance2.setHost("2.2.2.2"); + instances.add((Instance) instance2); + + ServiceInstances serviceInstances = new DefaultServiceInstances(new ServiceKey(), instances); + RouteResult result = namespaceRouter.router(routeInfo, serviceInstances); + assertThat(result.getInstances().size()).isEqualTo(2); + for (Instance instance : result.getInstances()) { + assertThat(instance.getNamespace()).isEqualTo(testNamespace1); + assertThat(instance.getHost()).isNotEqualTo("2.2.2.2"); + } + } + + @Test + public void test2() { + SourceService sourceService = new SourceService(); + sourceService.setNamespace(testNamespace1); + RouteInfo routeInfo = new RouteInfo(sourceService, null, null, null); + + List instances = new ArrayList<>(); + DefaultBaseInstance instance0 = new DefaultInstance(); + instance0.setNamespace(testNamespace2); + instance0.setHost("0.0.0.0"); + instances.add((Instance) instance0); + DefaultBaseInstance instance1 = new DefaultInstance(); + instance1.setNamespace(testNamespace2); + instance1.setHost("1.1.1.1"); + instances.add((Instance) instance1); + DefaultBaseInstance instance2 = new DefaultInstance(); + instance2.setNamespace(testNamespace2); + instance2.setHost("2.2.2.2"); + instances.add((Instance) instance2); + + ServiceInstances serviceInstances = new DefaultServiceInstances(new ServiceKey(), instances); + RouteResult result = namespaceRouter.router(routeInfo, serviceInstances); + assertThat(result.getInstances().size()).isEqualTo(3); + for (Instance instance : result.getInstances()) { + assertThat(instance.getNamespace()).isEqualTo(testNamespace2); + } + } + + @Test + public void test3() { + SourceService sourceService = new SourceService(); + sourceService.setNamespace(testNamespace1); + RouteInfo routeInfo = new RouteInfo(sourceService, null, null, null); + routeInfo.setNamespaceRouterFailoverType(NamespaceRouterFailoverType.none); + + List instances = new ArrayList<>(); + DefaultBaseInstance instance0 = new DefaultInstance(); + instance0.setNamespace(testNamespace2); + instance0.setHost("0.0.0.0"); + instances.add((Instance) instance0); + DefaultBaseInstance instance1 = new DefaultInstance(); + instance1.setNamespace(testNamespace2); + instance1.setHost("1.1.1.1"); + instances.add((Instance) instance1); + DefaultBaseInstance instance2 = new DefaultInstance(); + instance2.setNamespace(testNamespace2); + instance2.setHost("2.2.2.2"); + instances.add((Instance) instance2); + + ServiceInstances serviceInstances = new DefaultServiceInstances(new ServiceKey(), instances); + RouteResult result = namespaceRouter.router(routeInfo, serviceInstances); + assertThat(result.getInstances().size()).isEqualTo(0); + } + + @Test + public void testAspect() { + assertThat(namespaceRouter.getAspect()).isEqualTo(ServiceRouter.Aspect.MIDDLE); + } + + @Test + public void testName() { + assertThat(namespaceRouter.getName()).isEqualTo(ServiceRouterConfig.DEFAULT_ROUTER_NAMESPACE); + } +} diff --git a/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java b/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java index ba6d611ad..bbd92c573 100644 --- a/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java +++ b/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java @@ -17,6 +17,7 @@ package com.tencent.polaris.plugins.router.nearby; +import com.google.protobuf.Any; import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; import com.tencent.polaris.api.config.plugin.PluginConfigProvider; import com.tencent.polaris.api.config.verify.Verifier; @@ -27,20 +28,25 @@ import com.tencent.polaris.api.plugin.common.PluginTypes; import com.tencent.polaris.api.plugin.common.ValueContext; import com.tencent.polaris.api.plugin.compose.Extensions; -import com.tencent.polaris.api.plugin.route.LocationLevel; import com.tencent.polaris.api.plugin.route.RouteInfo; import com.tencent.polaris.api.plugin.route.RouteResult; -import com.tencent.polaris.api.pojo.Instance; -import com.tencent.polaris.api.pojo.ServiceInstances; -import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.pojo.*; +import com.tencent.polaris.api.rpc.RequestBaseEntity; import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.CompareUtils; import com.tencent.polaris.api.utils.MapUtils; import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; import com.tencent.polaris.logging.LoggerFactory; import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import org.slf4j.Logger; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static com.tencent.polaris.client.util.Utils.isHealthyInstance; @@ -61,20 +67,21 @@ public class NearbyRouter extends AbstractServiceRouter implements PluginConfigP private static final String NEARBY_METADATA_ENABLE = "internal-enable-nearby"; - private static final Logger LOG = LoggerFactory.getLogger(NearbyRouter.class); - private static final LocationLevel defaultMinLevel = LocationLevel.zone; + private static final RoutingProto.NearbyRoutingConfig.LocationLevel defaultMinLevel = RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE; /** * 主调的地域信息 */ - private final AtomicReference> locationInfo = new AtomicReference<>(); + private final AtomicReference> locationInfo = new AtomicReference<>(); /** * 等待地域信息就绪的超时时间 */ long locationReadyTimeout; private ValueContext valueContext; + private final AtomicBoolean firstRoute = new AtomicBoolean(true); + /** * # 默认就近区域:默认城市 matchLevel: zone # 最大就近区域,默认为空(全匹配) maxMatchLevel: all # * 假如开启了严格就近,插件的初始化会等待地域信息获取成功才返回,假如获取失败(server获取失败或者IP地域信息缺失),则会初始化失败,而且必须按照 strictNearby: false # @@ -90,12 +97,42 @@ public class NearbyRouter extends AbstractServiceRouter implements PluginConfigP @Override public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances) throws PolarisException { + if (firstRoute.compareAndSet(true, false)) { + refreshLocationInfo(); + } + + ServiceRule serviceRule = getServiceRule(serviceInstances.getNamespace(), serviceInstances.getService()); + RoutingProto.NearbyRoutingConfig nearbyRoutingConfig = null; + if (serviceRule != null && serviceRule.getRule() != null) { + ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) serviceRule.getRule(); + List nearByRouteRuleList = sortNearbyRouteRules(discoverResponse.getNearbyRouteRulesList()); + if (CollectionUtils.isNotEmpty(nearByRouteRuleList)) { + Any any = null; + for (RoutingProto.RouteRule routeRule : nearByRouteRuleList) { + if (routeRule.getEnable()) { + any = routeRule.getRoutingConfig(); + break; + } + } + if (any != null) { + try { + nearbyRoutingConfig = any.unpack(RoutingProto.NearbyRoutingConfig.class); + } catch (Exception e) { + LOG.warn("{} cannot be unpacked to an instance of RoutingProto.NearbyRoutingConfig", any); + } + } + } + } + //先获取最低可用就近级别 - LocationLevel minAvailableLevel = config.getMatchLevel(); + RoutingProto.NearbyRoutingConfig.LocationLevel minAvailableLevel = config.getMatchLevel(); if (null == minAvailableLevel) { minAvailableLevel = defaultMinLevel; } - LocationLevel minLevel = minAvailableLevel; + if (nearbyRoutingConfig != null && nearbyRoutingConfig.getMatchLevel() != RoutingProto.NearbyRoutingConfig.LocationLevel.UNKNOWN) { + minAvailableLevel = nearbyRoutingConfig.getMatchLevel(); + } + RoutingProto.NearbyRoutingConfig.LocationLevel minLevel = minAvailableLevel; if (null != routeInfo.getNextRouterInfo()) { if (null != routeInfo.getNextRouterInfo().getLocationLevel()) { minLevel = routeInfo.getNextRouterInfo().getLocationLevel(); @@ -104,12 +141,15 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances minAvailableLevel = routeInfo.getNextRouterInfo().getMinAvailableLevel(); } } - LocationLevel maxLevel = config.getMaxMatchLevel(); + RoutingProto.NearbyRoutingConfig.LocationLevel maxLevel = config.getMaxMatchLevel(); if (null == maxLevel) { - maxLevel = LocationLevel.all; + maxLevel = RoutingProto.NearbyRoutingConfig.LocationLevel.ALL; + } + if (nearbyRoutingConfig != null && nearbyRoutingConfig.getMaxMatchLevel() != RoutingProto.NearbyRoutingConfig.LocationLevel.UNKNOWN) { + maxLevel = nearbyRoutingConfig.getMaxMatchLevel(); } - Map clientLocationInfo = getLocationInfo(); + Map clientLocationInfo = getLocationInfo(); if (minLevel.ordinal() >= maxLevel.ordinal()) { List instances = selectInstances(serviceInstances, minAvailableLevel, clientLocationInfo); if (CollectionUtils.isEmpty(instances)) { @@ -122,7 +162,7 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances } CheckResult checkResult = new CheckResult(); for (int i = minLevel.ordinal(); i <= maxLevel.ordinal(); i++) { - LocationLevel curLevel = LocationLevel.values()[i]; + RoutingProto.NearbyRoutingConfig.LocationLevel curLevel = RoutingProto.NearbyRoutingConfig.LocationLevel.values()[i]; checkResult = hasHealthyInstances(serviceInstances, curLevel, clientLocationInfo); checkResult.curLevel = curLevel; @@ -136,7 +176,7 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances throw new PolarisException(ErrorCode.LOCATION_MISMATCH, String.format("can not find any instance by level %s", checkResult.curLevel.name())); } - if (!config.isEnableDegradeByUnhealthyPercent() || checkResult.curLevel == LocationLevel.all) { + if (!config.isEnableDegradeByUnhealthyPercent() || checkResult.curLevel == RoutingProto.NearbyRoutingConfig.LocationLevel.ALL) { return new RouteResult(checkResult.instances, RouteResult.State.Next); } int healthyInstanceCount = checkResult.healthyInstanceCount; @@ -155,27 +195,70 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances return new RouteResult(checkResult.instances, RouteResult.State.Next); } - private LocationLevel nextLevel(LocationLevel current) { - if (current == LocationLevel.all) { + private List sortNearbyRouteRules(List rules) { + List sorted = new ArrayList<>(rules); + // 数字越小,规则优先级越大 + sorted.sort(Comparator.comparingInt(RoutingProto.RouteRule::getPriority)); + sorted.sort((o1, o2) -> { + // 比较优先级,数字越小,规则优先级越大 + int priorityResult = o1.getPriority() - o2.getPriority(); + if (priorityResult != 0) { + return priorityResult; + } + + // 比较目标服务 + String destNamespace1 = ""; + String destService1 = ""; + try { + RoutingProto.NearbyRoutingConfig nearbyRoutingConfig = o1.getRoutingConfig().unpack(RoutingProto.NearbyRoutingConfig.class); + destNamespace1 = nearbyRoutingConfig.getNamespace(); + destService1 = nearbyRoutingConfig.getService(); + } catch (Exception e) { + LOG.warn("{} cannot be unpacked to an instance of RoutingProto.NearbyRoutingConfig", o1); + } + + String destNamespace2 = ""; + String destService2 = ""; + try { + RoutingProto.NearbyRoutingConfig nearbyRoutingConfig = o2.getRoutingConfig().unpack(RoutingProto.NearbyRoutingConfig.class); + destNamespace2 = nearbyRoutingConfig.getNamespace(); + destService2 = nearbyRoutingConfig.getService(); + } catch (Exception e) { + LOG.warn("{} cannot be unpacked to an instance of RoutingProto.NearbyRoutingConfig", o2); + } + int serviceKeyResult = CompareUtils.compareService(destNamespace1, destService1, destNamespace2, destService2); + if (serviceKeyResult != 0) { + return serviceKeyResult; + } + + String ruleId1 = o1.getId(); + String ruleId2 = o1.getId(); + return CompareUtils.compareSingleValue(ruleId1, ruleId2); + }); + return sorted; + } + + private RoutingProto.NearbyRoutingConfig.LocationLevel nextLevel(RoutingProto.NearbyRoutingConfig.LocationLevel current) { + if (current == RoutingProto.NearbyRoutingConfig.LocationLevel.ALL) { return current; } - return LocationLevel.values()[current.ordinal() + 1]; + return RoutingProto.NearbyRoutingConfig.LocationLevel.values()[current.ordinal() + 1]; } private CheckResult hasHealthyInstances(ServiceInstances svcInstances, - LocationLevel targetLevel, Map clientInfo) { + RoutingProto.NearbyRoutingConfig.LocationLevel targetLevel, Map clientInfo) { String clientZone = ""; String clientRegion = ""; String clientCampus = ""; if (null != clientInfo) { - clientZone = clientInfo.getOrDefault(LocationLevel.zone, ""); - clientRegion = clientInfo.getOrDefault(LocationLevel.region, ""); - clientCampus = clientInfo.getOrDefault(LocationLevel.campus, ""); + clientZone = clientInfo.getOrDefault(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE, ""); + clientRegion = clientInfo.getOrDefault(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION, ""); + clientCampus = clientInfo.getOrDefault(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS, ""); } CheckResult checkResult = new CheckResult(); for (Instance instance : svcInstances.getInstances()) { switch (targetLevel) { - case zone: + case ZONE: if (clientZone.equals("") || clientZone.equals(getInstanceZone(instance))) { checkResult.instances.add(instance); if (isHealthyInstance(instance)) { @@ -183,7 +266,7 @@ private CheckResult hasHealthyInstances(ServiceInstances svcInstances, } } break; - case campus: + case CAMPUS: if (clientCampus.equals("") || clientCampus.equals(getInstanceCampus(instance))) { checkResult.instances.add(instance); if (isHealthyInstance(instance)) { @@ -191,7 +274,7 @@ private CheckResult hasHealthyInstances(ServiceInstances svcInstances, } } break; - case region: + case REGION: if (clientRegion.equals("") || clientRegion.equals(getInstanceRegion(instance))) { checkResult.instances.add(instance); if (isHealthyInstance(instance)) { @@ -211,29 +294,30 @@ private CheckResult hasHealthyInstances(ServiceInstances svcInstances, } private List selectInstances( - ServiceInstances svcInstances, LocationLevel targetLevel, Map clientInfo) { + ServiceInstances svcInstances, RoutingProto.NearbyRoutingConfig.LocationLevel targetLevel, + Map clientInfo) { List instances = new ArrayList<>(); String clientZone = ""; String clientRegion = ""; String clientCampus = ""; if (null != clientInfo) { - clientZone = clientInfo.get(LocationLevel.zone); - clientRegion = clientInfo.get(LocationLevel.region); - clientCampus = clientInfo.get(LocationLevel.campus); + clientZone = clientInfo.get(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE); + clientRegion = clientInfo.get(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION); + clientCampus = clientInfo.get(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS); } for (Instance instance : svcInstances.getInstances()) { switch (targetLevel) { - case zone: + case ZONE: if (clientZone.equals("") || clientZone.equals(getInstanceZone(instance))) { instances.add(instance); } break; - case campus: + case CAMPUS: if (clientCampus.equals("") || clientCampus.equals(getInstanceCampus(instance))) { instances.add(instance); } break; - case region: + case REGION: if (clientRegion.equals("") || clientRegion.equals(getInstanceRegion(instance))) { instances.add(instance); } @@ -286,6 +370,7 @@ public void init(InitContext ctx) throws PolarisException { */ @Override public void postContextInit(Extensions extensions) throws PolarisException { + this.extensions = extensions; //强制就近模式下,需要等待地域信息初始化成功 ensureLocationReady(); } @@ -308,8 +393,8 @@ public void ensureLocationReady() throws PolarisException { } private void refreshLocationInfo() { - Map clientLocationInfo = new HashMap<>(); - for (LocationLevel key : LocationLevel.values()) { + Map clientLocationInfo = new HashMap<>(); + for (RoutingProto.NearbyRoutingConfig.LocationLevel key : RoutingProto.NearbyRoutingConfig.LocationLevel.values()) { if (valueContext.getValue(key.name()) != null) { clientLocationInfo.put(key, valueContext.getValue(key.name())); } @@ -318,7 +403,7 @@ private void refreshLocationInfo() { LOG.debug("[refreshLocationInfo] locationInfo={}", clientLocationInfo); } - private Map getLocationInfo() { + private Map getLocationInfo() { if (MapUtils.isEmpty(locationInfo.get())) { refreshLocationInfo(); } @@ -335,11 +420,24 @@ public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { if (!super.enable(routeInfo, dstSvcInfo)) { return false; } - Map clientLocationInfo = getLocationInfo(); + Map clientLocationInfo = getLocationInfo(); if (MapUtils.isEmpty(clientLocationInfo)) { return false; } + ServiceRule serviceRule = getServiceRule(dstSvcInfo.getNamespace(), dstSvcInfo.getService()); + if (serviceRule != null && serviceRule.getRule() != null) { + ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) serviceRule.getRule(); + List nearByRouteRuleList = sortNearbyRouteRules(discoverResponse.getNearbyRouteRulesList()); + if (CollectionUtils.isNotEmpty(nearByRouteRuleList)) { + for (RoutingProto.RouteRule routeRule : nearByRouteRuleList) { + if (routeRule.getEnable()) { + return true; + } + } + } + } + Map destSvcMetadata = Optional.ofNullable(dstSvcInfo.getMetadata()).orElse(Collections.emptyMap()); if (Boolean.parseBoolean(destSvcMetadata.get(NEARBY_METADATA_ENABLE))) { return true; @@ -367,6 +465,27 @@ public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { protected void doDestroy() { } + /** + * 获取就近路由规则 + * + * @param dstNamespace 目标服务命名空间 + * @param dstServiceName 目标服务名 + * @return 目标服务就近路由规则 + */ + private ServiceRule getServiceRule(String dstNamespace, String dstServiceName) { + DefaultFlowControlParam engineFlowControlParam = new DefaultFlowControlParam(); + BaseFlow.buildFlowControlParam(new RequestBaseEntity(), extensions.getConfiguration(), engineFlowControlParam); + Set routerKeys = new HashSet<>(); + ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(dstNamespace, dstServiceName), + ServiceEventKey.EventType.NEARBY_ROUTE_RULE); + routerKeys.add(dstSvcEventKey); + DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider(); + svcKeysProvider.setSvcEventKeys(routerKeys); + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(extensions, false, svcKeysProvider, engineFlowControlParam); + return resourcesResponse.getServiceRule(dstSvcEventKey); + } + private String getInstanceZone(Instance instance) { String zone = instance.getZone(); if (StringUtils.isNotBlank(zone)) { @@ -401,7 +520,7 @@ private String getMetadata(Instance instance, String key) { private static class CheckResult { - LocationLevel curLevel; + RoutingProto.NearbyRoutingConfig.LocationLevel curLevel; int healthyInstanceCount; List instances = new ArrayList<>(); } diff --git a/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java b/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java index d477b8de8..0a636b34a 100644 --- a/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java +++ b/polaris-plugins/polaris-plugins-router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java @@ -18,8 +18,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.tencent.polaris.api.config.verify.Verifier; -import com.tencent.polaris.api.plugin.route.LocationLevel; import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; /** * 就近路由配置结构 @@ -33,10 +33,10 @@ public class NearbyRouterConfig implements Verifier { private Boolean enableReportLocalAddress; @JsonProperty - private LocationLevel matchLevel; + private RoutingProto.NearbyRoutingConfig.LocationLevel matchLevel; @JsonProperty - private LocationLevel maxMatchLevel; + private RoutingProto.NearbyRoutingConfig.LocationLevel maxMatchLevel; @JsonProperty private Boolean strictNearby; @@ -47,19 +47,19 @@ public class NearbyRouterConfig implements Verifier { @JsonProperty private Integer unhealthyPercentToDegrade; - public LocationLevel getMatchLevel() { + public RoutingProto.NearbyRoutingConfig.LocationLevel getMatchLevel() { return matchLevel; } - public void setMatchLevel(LocationLevel matchLevel) { + public void setMatchLevel(RoutingProto.NearbyRoutingConfig.LocationLevel matchLevel) { this.matchLevel = matchLevel; } - public LocationLevel getMaxMatchLevel() { + public RoutingProto.NearbyRoutingConfig.LocationLevel getMaxMatchLevel() { return maxMatchLevel; } - public void setMaxMatchLevel(LocationLevel maxMatchLevel) { + public void setMaxMatchLevel(RoutingProto.NearbyRoutingConfig.LocationLevel maxMatchLevel) { this.maxMatchLevel = maxMatchLevel; } diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java index d77628060..1e69c4afc 100644 --- a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java @@ -22,6 +22,7 @@ import com.tencent.polaris.api.pojo.ServiceInstances; import com.tencent.polaris.api.pojo.SourceService; import com.tencent.polaris.api.rpc.MetadataFailoverType; +import com.tencent.polaris.api.rpc.NamespaceRouterFailoverType; import com.tencent.polaris.api.rpc.RequestBaseEntity; import com.tencent.polaris.api.rpc.RuleBasedRouterFailoverType; import com.tencent.polaris.api.utils.CollectionUtils; @@ -53,6 +54,8 @@ public class ProcessRoutersRequest extends RequestBaseEntity { private MetadataFailoverType metadataFailoverType; // 规则路由降级策略 private RuleBasedRouterFailoverType ruleBasedRouterFailoverType; + // 命名空间就近路由降级类型 + private NamespaceRouterFailoverType namespaceRouterFailoverType; /** * 北极星内部治理规则执行时,会识别规则中的参数来源类别,如果发现规则中的参数来源指定为外部数据源时,会调用本接口进行获取 @@ -159,6 +162,14 @@ public void setMetadataContainerGroup(MetadataContainerGroup metadataContainerGr this.metadataContainerGroup = metadataContainerGroup; } + public NamespaceRouterFailoverType getNamespaceRouterFailoverType() { + return namespaceRouterFailoverType; + } + + public void setNamespaceRouterFailoverType(NamespaceRouterFailoverType namespaceRouterFailoverType) { + this.namespaceRouterFailoverType = namespaceRouterFailoverType; + } + private void buildRouterArgumentsBySourceService() { if (CollectionUtils.isEmpty(routerArgument)) { routerArgument = new HashMap<>(); diff --git a/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/flow/DefaultRouterFlow.java b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/flow/DefaultRouterFlow.java index 37b238580..f099ba83c 100644 --- a/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/flow/DefaultRouterFlow.java +++ b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/flow/DefaultRouterFlow.java @@ -105,6 +105,9 @@ public ProcessRoutersResponse processRouters(ProcessRoutersRequest request) { if (request.getMetadataContainerGroup() != null) { routeInfo.setMetadataContainerGroup(request.getMetadataContainerGroup()); } + if (request.getNamespaceRouterFailoverType() != null) { + routeInfo.setNamespaceRouterFailoverType(request.getNamespaceRouterFailoverType()); + } //获取路由规则 DefaultFlowControlParam engineFlowControlParam = new DefaultFlowControlParam(); BaseFlow.buildFlowControlParam(request, config, engineFlowControlParam); diff --git a/pom.xml b/pom.xml index 0171ff0fb..03debaf7a 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ 1.8 1.8 2.14.2 - 1.5.1 + 2.0.0.0-SNAPSHOT 1.4.5 2.9.1 2.1.1