diff --git a/polaris-common/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java b/polaris-common/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java index 656e560ed..a174db295 100644 --- a/polaris-common/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java +++ b/polaris-common/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java @@ -33,29 +33,21 @@ 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.DefaultServiceEventKeysProvider; -import com.tencent.polaris.api.pojo.Instance; -import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.*; import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; -import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; -import com.tencent.polaris.api.pojo.ServiceInfo; -import com.tencent.polaris.api.pojo.ServiceInstances; -import com.tencent.polaris.api.pojo.ServiceInstancesWrap; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.pojo.ServiceRule; -import com.tencent.polaris.api.pojo.Services; import com.tencent.polaris.api.rpc.Criteria; import com.tencent.polaris.api.rpc.RequestBaseEntity; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.client.util.Utils; import com.tencent.polaris.logging.LoggerFactory; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import org.slf4j.Logger; + +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.slf4j.Logger; + +import static com.tencent.polaris.api.plugin.route.RouterConstants.ROUTER_FAULT_TOLERANCE_ENABLE; /** * 同步调用流程 @@ -71,16 +63,16 @@ public class BaseFlow { /** * 通用获取单个服务实例的方法,用于SDK内部调用 * - * @param extensions 插件上下文 - * @param serviceKey 服务信息 + * @param extensions 插件上下文 + * @param serviceKey 服务信息 * @param coreRouterNames 核心路由插件链 - * @param lbPolicy 负载均衡策略 - * @param protocol 协议信息 - * @param hashKey 一致性hash的key + * @param lbPolicy 负载均衡策略 + * @param protocol 协议信息 + * @param hashKey 一致性hash的key * @return 过滤后的实例 */ public static Instance commonGetOneInstance(Extensions extensions, ServiceKey serviceKey, - List coreRouterNames, String lbPolicy, String protocol, String hashKey) { + List coreRouterNames, String lbPolicy, String protocol, String hashKey) { ServiceEventKey svcEventKey = new ServiceEventKey(serviceKey, EventType.INSTANCE); LOG.debug("[ConnectionManager]start to discover service {}", svcEventKey); DefaultServiceEventKeysProvider provider = new DefaultServiceEventKeysProvider(); @@ -123,14 +115,14 @@ public static Instance commonGetOneInstance(Extensions extensions, ServiceKey se /** * 处理服务路由 * - * @param routeInfo 路由信息 - * @param dstInstances 目标实例列表 + * @param routeInfo 路由信息 + * @param dstInstances 目标实例列表 * @param routerChainGroup 插件链 * @return 过滤后的实例 * @throws PolarisException 异常 */ public static ServiceInstances processServiceRouters(RouteInfo routeInfo, ServiceInstances dstInstances, - RouterChainGroup routerChainGroup) throws PolarisException { + RouterChainGroup routerChainGroup) throws PolarisException { if (null == dstInstances || CollectionUtils.isEmpty(dstInstances.getInstances())) { return dstInstances; } @@ -141,10 +133,19 @@ public static ServiceInstances processServiceRouters(RouteInfo routeInfo, Servic if (processRouterChain(routerChainGroup.getBeforeRouters(), routeInfo, serviceInstancesWrap)) { processed = true; } + Map destSvcMetadata = Optional.ofNullable(serviceInstancesWrap.getMetadata()).orElse(Collections.emptyMap()); + List faultToleranceServiceInstances = new ArrayList<>(); + if (Boolean.parseBoolean(destSvcMetadata.get(ROUTER_FAULT_TOLERANCE_ENABLE))) { + faultToleranceServiceInstances = new ArrayList<>(dstInstances.getInstances()); + } //再走业务路由 if (processRouterChain(routerChainGroup.getCoreRouters(), routeInfo, serviceInstancesWrap)) { processed = true; } + if (CollectionUtils.isEmpty(serviceInstancesWrap.getInstances()) + && Boolean.parseBoolean(destSvcMetadata.get(ROUTER_FAULT_TOLERANCE_ENABLE))) { + serviceInstancesWrap.setInstances(faultToleranceServiceInstances); + } //最后走后置路由 if (processRouterChain(routerChainGroup.getAfterRouters(), routeInfo, serviceInstancesWrap)) { processed = true; @@ -156,7 +157,7 @@ public static ServiceInstances processServiceRouters(RouteInfo routeInfo, Servic } private static boolean processRouterChain(List routers, - RouteInfo routeInfo, ServiceInstancesWrap serviceInstances) throws PolarisException { + RouteInfo routeInfo, ServiceInstancesWrap serviceInstances) throws PolarisException { if (CollectionUtils.isEmpty(routers)) { return false; } @@ -190,15 +191,15 @@ private static boolean processRouterChain(List routers, /** * 同步拉取资源数据 * - * @param extensions 插件集合 + * @param extensions 插件集合 * @param internalRequest 是否内部请求 - * @param paramProvider 参数提供器 - * @param controlParam 控制参数 + * @param paramProvider 参数提供器 + * @param controlParam 控制参数 * @return 多资源应答 * @throws PolarisException 获取异常 */ public static ResourcesResponse syncGetResources(Extensions extensions, boolean internalRequest, - ServiceEventKeysProvider paramProvider, FlowControlParam controlParam) + ServiceEventKeysProvider paramProvider, FlowControlParam controlParam) throws PolarisException { if (CollectionUtils.isEmpty(paramProvider.getSvcEventKeys()) && null == paramProvider.getSvcEventKey()) { @@ -245,7 +246,7 @@ public static ResourcesResponse syncGetResources(Extensions extensions, boolean } private static boolean readResourcesFromLocalCache(ServiceEventKeysProvider paramProvider, - Extensions extensions, ResourcesResponse resourcesResponse) { + Extensions extensions, ResourcesResponse resourcesResponse) { LocalRegistry localRegistry = extensions.getLocalRegistry(); if (null != paramProvider.getSvcEventKey()) { if (loadLocalResources(paramProvider.getSvcEventKey(), resourcesResponse, localRegistry)) { @@ -263,7 +264,7 @@ private static boolean readResourcesFromLocalCache(ServiceEventKeysProvider para } private static boolean loadLocalResources(ServiceEventKey svcEventKey, ResourcesResponse resourcesResponse, - LocalRegistry localRegistry) { + LocalRegistry localRegistry) { ResourceFilter filter = new ResourceFilter(svcEventKey, false, true); if (svcEventKey.getEventType() == EventType.INSTANCE) { ServiceInstances instances = localRegistry.getInstances(filter); @@ -293,7 +294,7 @@ private static boolean loadLocalResources(ServiceEventKey svcEventKey, Resources } public static Instance processLoadBalance(LoadBalancer loadBalancer, Criteria criteria, - ServiceInstances dstInstances) throws PolarisException { + ServiceInstances dstInstances) throws PolarisException { Instance instance = loadBalancer.chooseInstance(criteria, dstInstances); if (null == instance) { throw new PolarisException(ErrorCode.INSTANCE_NOT_FOUND, @@ -306,12 +307,12 @@ public static Instance processLoadBalance(LoadBalancer loadBalancer, Criteria cr /** * 构建流程控制参数 * - * @param entity 请求对象 - * @param config 配置对象 + * @param entity 请求对象 + * @param config 配置对象 * @param controlParam 控制参数 */ public static void buildFlowControlParam(RequestBaseEntity entity, Configuration config, - FlowControlParam controlParam) { + FlowControlParam controlParam) { long timeoutMs = entity.getTimeoutMs(); if (timeoutMs == 0) { timeoutMs = config.getGlobal().getAPI().getTimeout(); 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 32b4aa3a1..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 @@ -142,7 +142,7 @@ consumer: #描述: 缓存插件名 type: inmemory #描述: 是否启用服务数据缓存 - serviceExpireEnable: true + serviceExpireEnable: false #描述: 服务过期淘汰时间 serviceExpireTime: 24h #描述: 服务定期同步刷新周期 @@ -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-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/MetadataConstants.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/MetadataConstants.java new file mode 100644 index 000000000..b88849250 --- /dev/null +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/MetadataConstants.java @@ -0,0 +1,46 @@ +/* + * 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.metadata.core.constant; + +/** + * Metadata constant for Polaris. + * + * @author Haotian Zhang + */ +public class MetadataConstants { + + /** + * local service namespace. + */ + public static final String LOCAL_NAMESPACE = "LOCAL_NAMESPACE"; + + /** + * local service name. + */ + public static final String LOCAL_SERVICE = "LOCAL_SERVICE"; + + /** + * local host ip. + */ + public static final String LOCAL_IP = "LOCAL_IP"; + + /** + * local host port. + */ + public static final String LOCAL_PORT = "LOCAL_PORT"; +} diff --git a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/TsfMetadataConstants.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/TsfMetadataConstants.java new file mode 100644 index 000000000..ba7dbeb86 --- /dev/null +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/constant/TsfMetadataConstants.java @@ -0,0 +1,73 @@ +/* + * 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.metadata.core.constant; + +/** + * Metadata constant for TSF. + * + * @author Haotian Zhang + */ +public final class TsfMetadataConstants { + /** + * tsf application id. + */ + public static String TSF_APPLICATION_ID = "TSF_APPLICATION_ID"; + + /** + * tsf program version. + */ + public static String TSF_PROG_VERSION = "TSF_PROG_VERSION"; + + /** + * tsf group id. + */ + public static String TSF_GROUP_ID = "TSF_GROUP_ID"; + + /** + * tsf namespace id. + */ + public static String TSF_NAMESPACE_ID = "TSF_NAMESPACE_ID"; + + /** + * tsf instance id. + */ + public static String TSF_INSTNACE_ID = "TSF_INSTNACE_ID"; + + /** + * tsf region. + */ + public static String TSF_REGION = "TSF_REGION"; + + /** + * tsf zone. + */ + public static String TSF_ZONE = "TSF_ZONE"; + + /** + * tsf SDK version. + */ + public static String TSF_SDK_VERSION = "TSF_SDK_VERSION"; + + /** + * tsf tags. + */ + public static String TSF_TAGS = "TSF_TAGS"; + + private TsfMetadataConstants() { + } +} diff --git a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CalleeMetadataContainerGroup.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CalleeMetadataContainerGroup.java new file mode 100644 index 000000000..4bc2cb166 --- /dev/null +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CalleeMetadataContainerGroup.java @@ -0,0 +1,59 @@ +/* + * 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.metadata.core.manager; + +import com.tencent.polaris.metadata.core.MetadataContainer; +import com.tencent.polaris.metadata.core.impl.MessageMetadataContainerImpl; +import com.tencent.polaris.metadata.core.impl.MetadataContainerImpl; + +import static com.tencent.polaris.metadata.core.manager.MetadataContext.DEFAULT_TRANSITIVE_PREFIX; + +public class CalleeMetadataContainerGroup implements MetadataContainerGroup { + + private final MetadataContainer messageMetadataContainer; + + private final static MetadataContainer applicationMetadataContainer = new MetadataContainerImpl(DEFAULT_TRANSITIVE_PREFIX); + + private final MetadataContainer customMetadataContainer; + + public CalleeMetadataContainerGroup(String transitivePrefix) { + assert null != transitivePrefix; + this.messageMetadataContainer = new MessageMetadataContainerImpl(transitivePrefix); + this.customMetadataContainer = new MetadataContainerImpl(transitivePrefix); + } + + @Override + public MetadataContainer getMessageMetadataContainer() { + return messageMetadataContainer; + } + + @Override + public MetadataContainer getApplicationMetadataContainer() { + return applicationMetadataContainer; + } + + public static MetadataContainer getStaticApplicationMetadataContainer() { + return applicationMetadataContainer; + } + + @Override + public MetadataContainer getCustomMetadataContainer() { + return customMetadataContainer; + } + +} diff --git a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CallerMetadataContainerGroup.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CallerMetadataContainerGroup.java new file mode 100644 index 000000000..a1373733d --- /dev/null +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/CallerMetadataContainerGroup.java @@ -0,0 +1,54 @@ +/* + * 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.metadata.core.manager; + +import com.tencent.polaris.metadata.core.MetadataContainer; +import com.tencent.polaris.metadata.core.impl.MessageMetadataContainerImpl; +import com.tencent.polaris.metadata.core.impl.MetadataContainerImpl; + +public class CallerMetadataContainerGroup implements MetadataContainerGroup { + + private final MetadataContainer messageMetadataContainer; + + private final MetadataContainer applicationMetadataContainer; + + private final MetadataContainer customMetadataContainer; + + public CallerMetadataContainerGroup(String transitivePrefix) { + assert null != transitivePrefix; + this.messageMetadataContainer = new MessageMetadataContainerImpl(transitivePrefix); + this.applicationMetadataContainer = new MetadataContainerImpl(transitivePrefix); + this.customMetadataContainer = new MetadataContainerImpl(transitivePrefix); + } + + @Override + public MetadataContainer getMessageMetadataContainer() { + return messageMetadataContainer; + } + + @Override + public MetadataContainer getApplicationMetadataContainer() { + return applicationMetadataContainer; + } + + @Override + public MetadataContainer getCustomMetadataContainer() { + return customMetadataContainer; + } + +} diff --git a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContainerGroup.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContainerGroup.java index 76e5d6ed5..10c53ff76 100644 --- a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContainerGroup.java +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContainerGroup.java @@ -18,34 +18,12 @@ package com.tencent.polaris.metadata.core.manager; import com.tencent.polaris.metadata.core.MetadataContainer; -import com.tencent.polaris.metadata.core.impl.MessageMetadataContainerImpl; -import com.tencent.polaris.metadata.core.impl.MetadataContainerImpl; -public class MetadataContainerGroup { +public interface MetadataContainerGroup { - private final MetadataContainer messageMetadataContainer; + MetadataContainer getMessageMetadataContainer(); - private final MetadataContainer applicationMetadataContainer; - - private final MetadataContainer customMetadataContainer; - - public MetadataContainerGroup(String transitivePrefix) { - assert null != transitivePrefix; - this.messageMetadataContainer = new MessageMetadataContainerImpl(transitivePrefix); - this.applicationMetadataContainer = new MetadataContainerImpl(transitivePrefix); - this.customMetadataContainer = new MetadataContainerImpl(transitivePrefix); - } - - public MetadataContainer getMessageMetadataContainer() { - return messageMetadataContainer; - } - - public MetadataContainer getApplicationMetadataContainer() { - return applicationMetadataContainer; - } - - public MetadataContainer getCustomMetadataContainer() { - return customMetadataContainer; - } + MetadataContainer getApplicationMetadataContainer(); + MetadataContainer getCustomMetadataContainer(); } diff --git a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContext.java b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContext.java index 9f5ed523d..db0f83aec 100644 --- a/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContext.java +++ b/polaris-common/polaris-metadata/src/main/java/com/tencent/polaris/metadata/core/manager/MetadataContext.java @@ -40,8 +40,8 @@ public MetadataContext(String transitivePrefix) { transitivePrefix = ""; } this.transitivePrefix = transitivePrefix; - callerMetadataContainerGroup = new MetadataContainerGroup(transitivePrefix); - calleeMetadataContainerGroup = new MetadataContainerGroup(transitivePrefix); + callerMetadataContainerGroup = new CallerMetadataContainerGroup(transitivePrefix); + calleeMetadataContainerGroup = new CalleeMetadataContainerGroup(transitivePrefix); } /** @@ -71,8 +71,15 @@ public T getMetadataContainer(MetadataType metadat } } + public MetadataContainerGroup getMetadataContainerGroup(boolean caller) { + if (caller) { + return callerMetadataContainerGroup; + } else { + return calleeMetadataContainerGroup; + } + } + public String getTransitivePrefix() { return transitivePrefix; } - } diff --git a/polaris-common/polaris-model/pom.xml b/polaris-common/polaris-model/pom.xml index ea79ab3f0..18a9c5bf2 100644 --- a/polaris-common/polaris-model/pom.xml +++ b/polaris-common/polaris-model/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-common com.tencent.polaris @@ -25,6 +25,11 @@ polaris-protobuf ${project.version} + + com.tencent.polaris + polaris-metadata + ${project.version} + com.tencent.polaris polaris-logging 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 5e3ccc871..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 @@ -18,6 +18,7 @@ package com.tencent.polaris.api.pojo; import com.tencent.polaris.api.utils.StringUtils; + import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -55,6 +56,8 @@ public class DefaultInstance extends DefaultBaseInstance implements Instance { private String logicSet; + private Map serviceMetadata = new HashMap<>(); + @Override public String getRevision() { return revision; @@ -185,6 +188,18 @@ public void setLogicSet(String logicSet) { this.logicSet = logicSet; } + public Map getServiceMetadata() { + return serviceMetadata; + } + + public void setServiceMetadata(Map serviceMetadata) { + if (serviceMetadata != null) { + this.serviceMetadata = serviceMetadata; + } else { + this.serviceMetadata = new HashMap<>(); + } + } + @Override public CircuitBreakerStatus getCircuitBreakerStatus() { return circuitBreakerStatuses.get(StatusDimension.EMPTY_DIMENSION); @@ -248,6 +263,7 @@ public String toString() { ", priority=" + priority + ", weight=" + weight + ", logicSet='" + logicSet + '\'' + + ", serviceMetadata=" + serviceMetadata + '}'; } } diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceInstances.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceInstances.java index 0f66917c3..3f4530de8 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceInstances.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceInstances.java @@ -19,11 +19,8 @@ import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.client.pojo.Node; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; + +import java.util.*; public class DefaultServiceInstances implements ServiceInstances { @@ -35,6 +32,8 @@ public class DefaultServiceInstances implements ServiceInstances { private final Map nodeMap; + private final Map metadata; + private final int totalWeight; private final int hashCode; @@ -42,6 +41,10 @@ public class DefaultServiceInstances implements ServiceInstances { private final String revision; public DefaultServiceInstances(ServiceKey serviceKey, List instances) { + this(serviceKey, instances, null); + } + + public DefaultServiceInstances(ServiceKey serviceKey, List instances, Map metadata) { this.serviceKey = serviceKey; this.instances = Collections.unmodifiableList(instances); this.totalWeight = getTotalWeight(instances); @@ -53,6 +56,10 @@ public DefaultServiceInstances(ServiceKey serviceKey, List instances) idMap.put(instance.getId(), instance); nodeMap.put(new Node(instance.getHost(), instance.getPort()), instance); } + this.metadata = new HashMap<>(); + if (CollectionUtils.isNotEmpty(metadata)) { + this.metadata.putAll(metadata); + } } private int getTotalWeight(List instances) { @@ -102,7 +109,7 @@ public Instance getInstance(String id) { @Override public Map getMetadata() { - return null; + return metadata; } @Override diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/HttpElement.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/HttpElement.java new file mode 100644 index 000000000..08fb92061 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/HttpElement.java @@ -0,0 +1,53 @@ +/* + * 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.pojo; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class HttpElement { + + + public final class HttpMethod { + + public static final String GET = "GET"; + public static final String HEAD = "HEAD"; + public static final String POST = "POST"; + public static final String PUT = "PUT"; + public static final String PATCH = "PATCH"; + public static final String DELETE = "DELETE"; + public static final String OPTIONS = "OPTIONS"; + public static final String TRACE = "TRACE"; + } + + public static final Set HTTP_METHOD_SET = + new HashSet<>(Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT, + HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.OPTIONS, HttpMethod.TRACE)); + + public final class MediaType { + + public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8"; + public static final String APPLICATION_XHTML_XML = "application/xhtml+xml"; + public static final String APPLICATION_XML = "application/xml;charset=UTF-8"; + public static final String APPLICATION_JSON = "application/json;charset=UTF-8"; + public static final String MULTIPART_FORM_DATA = "multipart/form-data;charset=UTF-8"; + public static final String TEXT_HTML = "text/html;charset=UTF-8"; + public static final String TEXT_PLAIN = "text/plain;charset=UTF-8"; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java index ce6e3bc55..fccb0f542 100644 --- a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java @@ -18,6 +18,7 @@ package com.tencent.polaris.api.pojo; import java.util.Collection; +import java.util.HashMap; import java.util.Map; /** @@ -82,6 +83,10 @@ public interface Instance extends BaseInstance, Comparable { String getLogicSet(); + default Map getServiceMetadata() { + return new HashMap<>(); + } + static Instance createDefaultInstance(String instId, String namespace, String service, String host, int port) { DefaultInstance defaultInstance = new DefaultInstance(); defaultInstance.setHealthy(true); 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/pojo/TrieNode.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/TrieNode.java new file mode 100644 index 000000000..9bfd41c09 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/TrieNode.java @@ -0,0 +1,73 @@ +/* + * 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.pojo; + +import java.util.HashMap; +import java.util.Map; + +public class TrieNode { + private final Map> children; + private final String path; + + // 只有叶子节点才有 + private T nodeInfo; + + // 增加非法字符,确保不会和前端传入的参数相同 + public static final String POLARIS_WILDCARD = "#polaris_wildcard#"; + + public static final String SIMPLE_VALID_INFO = "#TSF#"; + + public static final String ROOT_PATH = ""; + + // root 构造器 + public TrieNode(String path) { + this.path = path; + this.children = new HashMap<>(); + } + + public TrieNode getSubNode(String nodeKey) { + if (children.containsKey(nodeKey)) { + return children.get(nodeKey); + } else if (children.containsKey(POLARIS_WILDCARD)) { + return children.get(POLARIS_WILDCARD); + } + + return null; + } + + // only for build trie + public TrieNode getOrCreateSubNode(String path) { + if (path.startsWith("{") && path.endsWith("}")) { + path = POLARIS_WILDCARD; + } + + if (!children.containsKey(path)) { + children.putIfAbsent(path, new TrieNode(path)); + } + + return children.get(path); + } + + public T getNodeInfo() { + return nodeInfo; + } + + public void setNodeInfo(T nodeInfo) { + this.nodeInfo = nodeInfo; + } +} \ No newline at end of file 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/ApiTrieUtil.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ApiTrieUtil.java new file mode 100644 index 000000000..ac2cecb84 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ApiTrieUtil.java @@ -0,0 +1,110 @@ +/* + * 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 com.tencent.polaris.api.pojo.HttpElement; +import com.tencent.polaris.api.pojo.TrieNode; + +public class ApiTrieUtil { + + /** + * @param apiPath + * @return TrieNode + */ + public static TrieNode buildSimpleTrieNode(String apiPath) { + if (StringUtils.isEmpty(apiPath)) { + return null; + } + return buildSimpleTrieNode(new String[]{apiPath}); + } + + public static TrieNode buildSimpleTrieNode(String[] apiPathInfoList) { + if (apiPathInfoList.length == 0) { + return null; + } + + TrieNode root = new TrieNode<>(TrieNode.ROOT_PATH); + for (String apiPathInfo : apiPathInfoList) { + int flag = apiPathInfo.lastIndexOf("-"); + String method = null; + String path = apiPathInfo; + if (flag != -1) { + method = apiPathInfo.substring(flag + 1); + path = apiPathInfo; + if (HttpElement.HTTP_METHOD_SET.contains(method)) { + path = apiPathInfo.substring(0, flag); + } else { + method = null; + } + } + + // 因为前端的改动(最初的 tagValue 只有 path,某次前端组件改动后变成了 path-method,非客户提的),有兼容性问题, + // 临时简化处理,不处理 method,前面逻辑保留是为了取出正确的 path + String[] apiPaths = path.split("/"); + + // 跳过第一个为空的str + TrieNode node = root; + for (int i = 1; i < apiPaths.length; i++) { + node = node.getOrCreateSubNode(apiPaths[i]); + + // 叶子节点,需要 info + if (i == apiPaths.length - 1) { + node.setNodeInfo(TrieNode.SIMPLE_VALID_INFO + "method:" + method); + } + } + } + return root; + } + + public static boolean checkSimple(TrieNode root, String apiPathInfo) { + if (root == null) { + return false; + } + int flag = apiPathInfo.lastIndexOf("-"); + String method = apiPathInfo.substring(flag + 1); + String path = apiPathInfo; + if (HttpElement.HTTP_METHOD_SET.contains(method)) { + path = apiPathInfo.substring(0, flag); + } else { + method = null; + } + // 因为前端的改动(最初的 tagValue 只有 path,某次前端组件改动后变成了 path-method,非客户提的),有兼容性问题, + // 临时简化处理,不处理 method,前面逻辑保留是为了取出正确的 path + method = null; + String[] apiPaths = path.split("/"); + + TrieNode node = root; + for (int i = 1; i < apiPaths.length; i++) { + if (node == null) { + return false; + } + node = node.getSubNode(apiPaths[i]); + // 叶子节点 + if (i == apiPaths.length - 1) { + if (node == null) { + return false; + } else { + return StringUtils.equals(TrieNode.SIMPLE_VALID_INFO + "method:" + method, node.getNodeInfo()); + } + } + } + + return false; + } + +} 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..6e3f912d4 --- /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 name.equals(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 bebb5a8d0..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,7 +18,10 @@ 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; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString.MatchStringType; import org.slf4j.Logger; @@ -34,6 +37,8 @@ public class RuleUtils { public static final String MATCH_ALL = "*"; + public static final String CALLEE_APPLICATION_METADATA_PREFIX = "$caller_metadata."; + private static final Function DEFAULT_REGEX_PATTERN = new Function() { @Override public Pattern apply(String s) { @@ -70,15 +75,15 @@ public static boolean matchStringValue(MatchString matchString, String actualVal Function regexToPattern) { MatchStringType matchType = matchString.getType(); String matchValue = matchString.getValue().getValue(); - return matchStringValue(matchType, actualValue, matchValue, regexToPattern); + return matchStringValue(matchType, actualValue, matchValue, regexToPattern, false, null); } public static boolean matchStringValue(MatchStringType matchType, String actualValue, String matchValue) { - return matchStringValue(matchType, actualValue, matchValue, DEFAULT_REGEX_PATTERN); + return matchStringValue(matchType, actualValue, matchValue, DEFAULT_REGEX_PATTERN, false, null); } private static boolean matchStringValue(MatchStringType matchType, String actualValue, String matchValue, - Function regexToPattern) { + Function regexToPattern, boolean useTrieNode, Function> trieNodeFunction) { actualValue = StringUtils.defaultString(actualValue); matchValue = StringUtils.defaultString(matchValue); if (RuleUtils.isMatchAllValue(matchValue)) { @@ -86,6 +91,9 @@ private static boolean matchStringValue(MatchStringType matchType, String actual } switch (matchType) { case EXACT: { + if (useTrieNode && trieNodeFunction != null) { + return ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue); + } return StringUtils.equals(actualValue, matchValue); } case REGEX: { @@ -94,13 +102,22 @@ private static boolean matchStringValue(MatchStringType matchType, String actual return pattern.matcher(actualValue).find(); } case NOT_EQUALS: { + if (useTrieNode && trieNodeFunction != null) { + return !ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue); + } return !StringUtils.equals(actualValue, matchValue); } case IN: { String[] tokens = matchValue.split(","); for (String token : tokens) { - if (StringUtils.equals(token, actualValue)) { - return true; + if (useTrieNode && trieNodeFunction != null) { + if (ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue)) { + return true; + } + } else { + if (StringUtils.equals(token, actualValue)) { + return true; + } } } return false; @@ -108,8 +125,14 @@ private static boolean matchStringValue(MatchStringType matchType, String actual case NOT_IN: { String[] tokens = matchValue.split(","); for (String token : tokens) { - if (StringUtils.equals(token, actualValue)) { - return false; + if (useTrieNode && trieNodeFunction != null) { + if (ApiTrieUtil.checkSimple(trieNodeFunction.apply(matchValue), actualValue)) { + return false; + } + } else { + if (StringUtils.equals(token, actualValue)) { + return false; + } } } return true; @@ -146,6 +169,14 @@ public static boolean matchMetadata(Map ruleMeta, Map ruleMeta, Map destMeta, boolean isMatchSource, Map multiEnvRouterParamMap, Map variables) { + return matchMetadata(ruleMeta, destMeta, null, isMatchSource, multiEnvRouterParamMap, variables, null); + } + + // 匹配metadata + public static boolean matchMetadata(Map ruleMeta, Map destMeta, + MetadataContainerGroup metadataContainerGroup, boolean isMatchSource, + Map multiEnvRouterParamMap, Map variables, + Function> trieNodeFunction) { // 如果规则metadata为空, 返回成功 if (MapUtils.isEmpty(ruleMeta)) { return true; @@ -154,7 +185,7 @@ public static boolean matchMetadata(Map ruleMeta, Map ruleMeta, Map ruleMeta, Map multiEnvRouterParamMap, - Map variables) { + Map variables, + boolean useTrieNode, Function> trieNodeFunction) { if (RuleUtils.MATCH_ALL.equals(destMetaValue)) { return true; } return matchValueByValueType(isMatchSource, ruleMetaKey, ruleMetaValue, destMetaValue, - multiEnvRouterParamMap, variables); + multiEnvRouterParamMap, variables, useTrieNode, trieNodeFunction); } private static boolean matchValueByValueType(boolean isMatchSource, String ruleMetaKey, MatchString ruleMetaValue, String destMetaValue, Map multiEnvRouterParamMap, - Map variables) { + Map variables, + boolean useTrieNode, Function> trieNodeFunction) { boolean allMetaMatched = true; switch (ruleMetaValue.getValueType()) { @@ -257,10 +314,27 @@ private static boolean matchValueByValueType(boolean isMatchSource, String ruleM break; default: allMetaMatched = matchStringValue(ruleMetaValue.getType(), destMetaValue, - ruleMetaValue.getValue().getValue()); + ruleMetaValue.getValue().getValue(), DEFAULT_REGEX_PATTERN, useTrieNode, trieNodeFunction); } 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/cache/CacheConstants.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/CacheConstants.java new file mode 100644 index 000000000..3ef04e6c1 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/CacheConstants.java @@ -0,0 +1,28 @@ +/* + * 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.cache; + +/** + * Constant for Cache. + * + * @author Haotian Zhang + */ +public interface CacheConstants { + + int API_ID = 433; +} 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/registry/CacheHandler.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java index 38e816c25..547d66edf 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java @@ -17,6 +17,7 @@ package com.tencent.polaris.api.plugin.registry; +import com.tencent.polaris.api.plugin.cache.FlowCache; import com.tencent.polaris.api.pojo.RegistryCacheValue; import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; @@ -63,12 +64,13 @@ enum CachedStatus { /** * 将服务端原始消息转换为缓存对象 * - * @param oldValue 旧值 - * @param newValue 新原始消息 + * @param oldValue 旧值 + * @param newValue 新原始消息 * @param isCacheLoaded 是否从本地缓存加载 + * @param flowCache 缓存 * @return 新缓存值 */ - RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded); + RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache); } 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 567b21417..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 @@ -17,23 +17,16 @@ package com.tencent.polaris.api.plugin.route; import com.tencent.polaris.api.config.provider.ServiceConfig; -import com.tencent.polaris.api.pojo.RouteArgument; -import com.tencent.polaris.api.pojo.ServiceInfo; -import com.tencent.polaris.api.pojo.ServiceMetadata; -import com.tencent.polaris.api.pojo.ServiceRule; -import com.tencent.polaris.api.pojo.SourceService; -import com.tencent.polaris.api.pojo.StatusDimension; +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; import com.tencent.polaris.specification.api.v1.model.ModelProto.MatchString; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; + +import java.util.*; import java.util.function.Function; /** @@ -68,9 +61,13 @@ public class RouteInfo { private MetadataFailoverType metadataFailoverType; //规则路由降级类型 private RuleBasedRouterFailoverType ruleBasedRouterFailoverType; + // 命名空间就近路由降级类型 + private NamespaceRouterFailoverType namespaceRouterFailoverType; //各个路由插件依赖的 metadata 参数 private final Map> routerMetadata = new HashMap<>(); + private MetadataContainerGroup metadataContainerGroup; + private Function> externalParameterSupplier = s -> Optional.empty(); /** @@ -85,15 +82,15 @@ public class RouteInfo { /** * 构造器 * - * @param sourceService 源服务 + * @param sourceService 源服务 * @param sourceRouteRule 源规则 - * @param destService 目标服务 - * @param destRouteRule 目标规则 - * @param method 接口名 - * @param serviceConfig 配置的当前服务名 + * @param destService 目标服务 + * @param destRouteRule 目标规则 + * @param method 接口名 + * @param serviceConfig 配置的当前服务名 */ public RouteInfo(SourceService sourceService, ServiceRule sourceRouteRule, - ServiceMetadata destService, ServiceRule destRouteRule, String method, ServiceConfig serviceConfig) { + ServiceMetadata destService, ServiceRule destRouteRule, String method, ServiceConfig serviceConfig) { if (isEmptyService(sourceService) && !isEmptyService(serviceConfig)) { this.sourceService = new SourceService(); this.sourceService.setNamespace(serviceConfig.getNamespace()); @@ -127,11 +124,11 @@ public RouteInfo(SourceService sourceService, ServiceRule sourceRouteRule, * 构造器 * * @param sourceService 源服务 - * @param destService 目标服务 - * @param method 接口名 + * @param destService 目标服务 + * @param method 接口名 */ public RouteInfo(SourceService sourceService, ServiceMetadata destService, String method, - ServiceConfig serviceConfig) { + ServiceConfig serviceConfig) { this(sourceService, null, destService, null, method, serviceConfig); } @@ -296,4 +293,19 @@ private static boolean isEmptyService(ServiceConfig serviceConfig) { return StringUtils.isBlank(serviceConfig.getNamespace()) && StringUtils.isBlank(serviceConfig.getName()); } + public MetadataContainerGroup getMetadataContainerGroup() { + return metadataContainerGroup; + } + + 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-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java index bdb8e1d0b..1670a2d49 100644 --- a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java @@ -25,4 +25,7 @@ public interface RouterConstants { //金丝雀路由 String CANARY_KEY = "canary"; + + // 服务路由容错 + String ROUTER_FAULT_TOLERANCE_ENABLE = "internal-enable-router-faulttolerance"; } 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 8872c35bc..afe22c64d 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,241 +37,248 @@ 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()); - for (Resource resource : copiedResources) { - if (!matchInstanceToResource(instance, resource)) { - continue; - } - ResourceStat resourceStat = new ResourceStat( - resource, detectResult.getStatusCode(), detectResult.getDelay(), detectResult.getRetStatus()); - HC_EVENT_LOG.info("report health check to resource {}, status code {}, delay {}", resource, - 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()); + for (Resource resource : copiedResources) { + if (!matchInstanceToResource(instance, resource)) { + continue; + } + ResourceStat resourceStat = new ResourceStat( + resource, detectResult.getStatusCode(), detectResult.getDelay(), detectResult.getRetStatus()); + HC_EVENT_LOG.info("report health check to resource {}, status code {}, delay {}", resource, + 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 0e2c3c6f7..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 @@ -68,8 +68,6 @@ public class CompositeServiceUpdateTask extends ServiceUpdateTask { private final InstanceListMeta instanceListMeta = new InstanceListMeta(); - private boolean isAsync = false; - private String mainConnectorType = SERVER_CONNECTOR_GRPC; private boolean ifMainConnectorTypeSet = false; @@ -82,13 +80,11 @@ public CompositeServiceUpdateTask(ServiceEventHandler handler, DestroyableServer for (DestroyableServerConnector sc : compositeConnector.getServerConnectors()) { if (SERVER_CONNECTOR_GRPC.equals(sc.getName()) && sc.isDiscoveryEnable()) { subServiceUpdateTaskMap.put(SERVER_CONNECTOR_GRPC, new GrpcServiceUpdateTask(serviceEventHandler, sc)); - isAsync = true; mainConnectorType = SERVER_CONNECTOR_GRPC; ifMainConnectorTypeSet = true; } if (SERVER_CONNECTOR_CONSUL.equals(sc.getName()) && sc.isDiscoveryEnable()) { subServiceUpdateTaskMap.put(SERVER_CONNECTOR_CONSUL, new ConsulServiceUpdateTask(serviceEventHandler, sc)); - isAsync = true; if (!ifMainConnectorTypeSet) { mainConnectorType = sc.getName(); ifMainConnectorTypeSet = true; @@ -97,18 +93,34 @@ public CompositeServiceUpdateTask(ServiceEventHandler handler, DestroyableServer } } + @Override + public boolean needUpdate() { + boolean compositeNeedUpdate = super.needUpdate(); + boolean subNeedUpdate = false; + for (ServiceUpdateTask serviceUpdateTask : subServiceUpdateTaskMap.values()) { + subNeedUpdate = subNeedUpdate || serviceUpdateTask.needUpdate(); + } + return compositeNeedUpdate && subNeedUpdate; + } + @Override public void execute() { + boolean isServiceUpdateTaskExecuted = false; for (ServiceUpdateTask serviceUpdateTask : subServiceUpdateTaskMap.values()) { if ((serviceUpdateTask.getTaskType() == ServiceUpdateTaskConstant.Type.FIRST && serviceUpdateTask.getTaskStatus() == Status.READY) || serviceUpdateTask.needUpdate()) { + isServiceUpdateTaskExecuted = true; serviceUpdateTask.setStatus(ServiceUpdateTaskConstant.Status.READY, ServiceUpdateTaskConstant.Status.RUNNING); serviceUpdateTask.execute(this); } } - if (isAsync && ifMainConnectorTypeSet + // 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.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 38811071f..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,6 +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.NearByRouteRuleService; +import com.tencent.polaris.plugins.connector.consul.service.router.RoutingService; import org.slf4j.Logger; import java.util.*; @@ -221,6 +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 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 c970ce892..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; @@ -63,7 +64,7 @@ public class InstanceService extends ConsulService { private static final Logger LOG = LoggerFactory.getLogger(InstanceService.class); - private final Map serviceConsulIndex = new ConcurrentHashMap<>(); + private final Map serviceConsulIndexMap = new ConcurrentHashMap<>(); public InstanceService(ConsulClient consulClient, ConsulRawClient consulRawClient, ConsulContext consulContext, String threadName, ObjectMapper mapper) { @@ -81,15 +82,15 @@ public void sendRequest(ServiceUpdateTask serviceUpdateTask) { UrlParameters tagParams = StringUtils.isNotBlank(tag) ? new SingleUrlParameters("tag", tag) : null; UrlParameters passingParams = onlyPassing ? new SingleUrlParameters("passing") : null; UrlParameters nsTypeParam = new SingleUrlParameters("nsType", "DEF_AND_GLOBAL"); - Long index = getServersConsulIndex(serviceId); + Long currentIndex = getServersConsulIndex(serviceId); int code = ServerCodes.DATA_NO_CHANGE; - QueryParams queryParams = new QueryParams(consulContext.getWaitTime(), index); + QueryParams queryParams = new QueryParams(consulContext.getWaitTime(), currentIndex); try { - LOG.debug("Begin Get service instances of {} sync", serviceId); + LOG.debug("Begin get service instances of {} sync", serviceId); HttpResponse rawResponse = consulRawClient.makeGetRequest("/v1/health/service/" + serviceId, tagParams, passingParams, tokenParam, nsTypeParam, queryParams); if (rawResponse != null) { - if (!index.equals(rawResponse.getConsulIndex())) { + if (!currentIndex.equals(rawResponse.getConsulIndex())) { code = ServerCodes.EXECUTE_SUCCESS; } LOG.debug("raw response: " + rawResponse.getContent() + " ; onlyPassing: " + onlyPassing); @@ -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()); } } @@ -153,7 +161,7 @@ public void sendRequest(ServiceUpdateTask serviceUpdateTask) { boolean svcDeleted = serviceUpdateTask.notifyServerEvent(serverEvent); // 即使无服务,也要更新 index if (rawResponse.getConsulIndex() != null) { - setServersConsulIndex(serviceId, index, rawResponse.getConsulIndex()); + setServersConsulIndex(serviceId, currentIndex, rawResponse.getConsulIndex()); } if (!svcDeleted) { serviceUpdateTask.addUpdateTaskSet(); @@ -176,7 +184,7 @@ public void sendRequest(ServiceUpdateTask serviceUpdateTask) { } private Long getServersConsulIndex(String serviceId) { - Long index = serviceConsulIndex.get(serviceId); + Long index = serviceConsulIndexMap.get(serviceId); if (index != null) { return index; } @@ -185,7 +193,7 @@ private Long getServersConsulIndex(String serviceId) { } private void setServersConsulIndex(String serviceId, Long lastIndex, Long newIndex) { - LOG.debug("serviceId: " + serviceId, "; lastIndex: " + lastIndex + "; newIndex: " + newIndex); - serviceConsulIndex.put(serviceId, newIndex); + LOG.debug("serviceId: {}; lastIndex: {}; newIndex: {}", serviceId, lastIndex, newIndex); + serviceConsulIndexMap.put(serviceId, newIndex); } } diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagCondition.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagCondition.java new file mode 100644 index 000000000..d6dc5ffec --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagCondition.java @@ -0,0 +1,120 @@ +/* + * 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.common; + +import java.io.Serializable; +import java.util.Objects; + +/** + * TSF标签规则实体 + * + * @author vanqfjiang + */ +public class TagCondition implements Serializable { + + private static final long serialVersionUID = -4620595359940020010L; + + /** + * 标签ID + */ + private String tagId; + /** + * 标签类型 + * 定义在TagConstant.TYPE + */ + private String tagType; + /** + * 标签名 + * 系统类型的标签名定义在TagConstant.SYSTEM_FIELD + */ + private String tagField; + /** + * 标签运算符 + * 定义在TagConstant.OPERATOR + */ + private String tagOperator; + /** + * 标签的被运算对象值 + */ + private String tagValue; + + + @Override + public String toString() { + return "TagCondition{" + + "tagId=" + tagId + + ", tagType='" + tagType + '\'' + + ", tagField='" + tagField + '\'' + + ", tagOperator='" + tagOperator + '\'' + + ", tagValue='" + tagValue + '\'' + + '}'; + } + + /** + * name by equals2 is avoid override hashCode + * + * @param other + * @return true if equals other + */ + public boolean equals2(TagCondition other) { + return Objects.equals(this.tagType, other.tagType) + && Objects.equals(this.tagField, other.tagField) + && Objects.equals(this.tagOperator, other.tagOperator) + && Objects.equals(this.tagValue, other.tagValue); + } + + public String getTagId() { + return tagId; + } + + public void setTagId(String tagId) { + this.tagId = tagId; + } + + public String getTagType() { + return tagType; + } + + public void setTagType(String tagType) { + this.tagType = tagType; + } + + public String getTagField() { + return tagField; + } + + public void setTagField(String tagField) { + this.tagField = tagField; + } + + public String getTagOperator() { + return tagOperator; + } + + public void setTagOperator(String tagOperator) { + this.tagOperator = tagOperator; + } + + public String getTagValue() { + return tagValue; + } + + public void setTagValue(String tagValue) { + this.tagValue = tagValue; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConditionUtil.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConditionUtil.java new file mode 100644 index 000000000..db9747a7b --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConditionUtil.java @@ -0,0 +1,69 @@ +/* + * 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.common; + +import com.tencent.polaris.api.utils.RuleUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.constant.TsfMetadataConstants; +import com.tencent.polaris.specification.api.v1.model.ModelProto; + +public class TagConditionUtil { + + public static ModelProto.MatchString.MatchStringType parseMatchStringType(TagCondition tagCondition) { + String tagOperator = tagCondition.getTagOperator(); + if (StringUtils.equals(tagOperator, TagConstant.OPERATOR.EQUAL)) { + // 匹配关系 等于 + return ModelProto.MatchString.MatchStringType.EXACT; + } else if (StringUtils.equals(tagOperator, TagConstant.OPERATOR.NOT_EQUAL)) { + // 匹配关系 不等于 + return ModelProto.MatchString.MatchStringType.NOT_EQUALS; + } else if (StringUtils.equals(tagOperator, TagConstant.OPERATOR.IN)) { + return ModelProto.MatchString.MatchStringType.IN; + } else if (StringUtils.equals(tagOperator, TagConstant.OPERATOR.NOT_IN)) { + return ModelProto.MatchString.MatchStringType.NOT_IN; + } else if (StringUtils.equals(tagOperator, TagConstant.OPERATOR.REGEX)) { + return ModelProto.MatchString.MatchStringType.REGEX; + } else { + return ModelProto.MatchString.MatchStringType.REGEX; + } + } + + public static String parseMetadataKey(String originalKey) { + if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.SOURCE_APPLICATION_ID)) { + // 系统标签 应用ID + return RuleUtils.CALLEE_APPLICATION_METADATA_PREFIX + TsfMetadataConstants.TSF_APPLICATION_ID; + } else if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.SOURCE_GROUP_ID)) { + // 系统标签 部署组ID + return RuleUtils.CALLEE_APPLICATION_METADATA_PREFIX + TsfMetadataConstants.TSF_GROUP_ID; + } else if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.SOURCE_CONNECTION_IP)) { + // 系统标签 发起方本地IP + return MessageMetadataContainer.LABEL_KEY_CALLER_IP; + } else if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.SOURCE_APPLICATION_VERSION)) { + // 系统标签 包版本 + return RuleUtils.CALLEE_APPLICATION_METADATA_PREFIX + TsfMetadataConstants.TSF_PROG_VERSION; + } else if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.DESTINATION_INTERFACE)) { + // 系统标签 被调用方API + return MessageMetadataContainer.LABEL_KEY_PATH; + } else if (StringUtils.equals(originalKey, TagConstant.SYSTEM_FIELD.REQUEST_HTTP_METHOD)) { + // 系统标签 HTTP METHOD + return MessageMetadataContainer.LABEL_KEY_METHOD; + } + return originalKey; + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConstant.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConstant.java new file mode 100644 index 000000000..78d604376 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/common/TagConstant.java @@ -0,0 +1,160 @@ +/* + * 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.common; + +/** + * TSF 标签常量 + * + * @author hongweizhu + */ +public class TagConstant { + + /** + * 标签类型 + * + * @author hongweizhu + */ + public static class TYPE { + /** + * 系统标签 + */ + public static final String SYSTEM = "S"; + /** + * 用户自定义标签 + */ + public static final String CUSTOM = "U"; + } + + /** + * 规则之间运算表达式的逻辑关系 + * + * @author juanyinyang + */ + public static class TagRuleRelation { + /** + * 与 + */ + public static final String AND = "AND"; + /** + * 或 + */ + public static final String OR = "OR"; + } + + /** + * 操作符 + * + * @author hongweizhu + */ + public static class OPERATOR { + /** + * 包含 + */ + public static final String IN = "IN"; + /** + * 不包含 + */ + public static final String NOT_IN = "NOT_IN"; + /** + * 等于 + */ + public static final String EQUAL = "EQUAL"; + /** + * 不等于 + */ + public static final String NOT_EQUAL = "NOT_EQUAL"; + /** + * 正则 + */ + public static final String REGEX = "REGEX"; + } + + /** + * 系统标签名 + * + * @author vanqfjiang + */ + public static class SYSTEM_FIELD { + /** + * 请求发起方的应用 ID + */ + public static final String SOURCE_APPLICATION_ID = "source.application.id"; + /** + * 请求发起方的应用版本号 + */ + public static final String SOURCE_APPLICATION_VERSION = "source.application.version"; + /** + * 请求发起方的实例 ID + */ + public static final String SOURCE_INSTANCE_ID = "source.instance.id"; + /** + * 请求发起方的部署组 ID + */ + public static final String SOURCE_GROUP_ID = "source.group.id"; + /** + * 请求发起方 IP + */ + public static final String SOURCE_CONNECTION_IP = "source.connection.ip"; + /** + * 请求发起方的服务名 + */ + public static final String SOURCE_SERVICE_NAME = "source.service.name"; + + /** + * 请求发起方的 Namespace/serviceName + */ + public static final String SOURCE_NAMESPACE_SERVICE_NAME = "source.namespace.service.name"; + + /** + * 请求发起方的服务 token,鉴权模块使用 + */ + public static final String SOURCE_SERVICE_TOKEN = "source.service.token"; + /** + * 请求发起方被它的上游调用的接口(如果有) + */ + public static final String SOURCE_INTERFACE = "source.interface"; + /** + * 请求接收方的服务名 + */ + public static final String DESTINATION_SERVICE_NAME = "destination.service.name"; + /** + * 请求接收方被调用的接口 + */ + public static final String DESTINATION_INTERFACE = "destination.interface"; + + /** + * 请求接收方的应用 + */ + public static final String DESTINATION_APPLICATION_ID = "destination.application.id"; + /** + * 请求接收方被调用的接口 + */ + public static final String DESTINATION_APPLICATION_VERSION = "destination.application.version"; + + /** + * 请求接收方的部署组 ID + */ + public static final String DESTINATION_GROUP_ID = "destination.group.id"; + + /** + * 请求所使用的 HTTP 方法 + */ + public static final String REQUEST_HTTP_METHOD = "request.http.method"; + + } +} 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/RoutingService.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RoutingService.java new file mode 100644 index 000000000..424192095 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/RoutingService.java @@ -0,0 +1,325 @@ +/* + * 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.core.type.TypeReference; +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.BoolValue; +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.api.utils.StringUtils; +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.common.TagConstant; +import com.tencent.polaris.plugins.connector.consul.service.router.entity.*; +import com.tencent.polaris.specification.api.v1.model.ModelProto; +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.Yaml; + +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; +import static com.tencent.polaris.api.plugin.route.RouterConstants.ROUTER_FAULT_TOLERANCE_ENABLE; +import static com.tencent.polaris.plugins.connector.consul.service.common.TagConditionUtil.parseMatchStringType; +import static com.tencent.polaris.plugins.connector.consul.service.common.TagConditionUtil.parseMetadataKey; + +/** + * @author Haotian Zhang + */ +public class RoutingService extends ConsulService { + + private static final Logger LOG = LoggerFactory.getLogger(RoutingService.class); + + private final Map routerRuleConsulIndexMap = new ConcurrentHashMap<>(); + + public RoutingService(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 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"); + RouterRuleKey routerRuleKey = new RouterRuleKey(); + routerRuleKey.setNamespace(namespace); + routerRuleKey.setService(service); + Long currentIndex = getRouterRuleConsulIndex(routerRuleKey); + QueryParams queryParams = new QueryParams(consulContext.getWaitTime(), currentIndex); + int code = ServerCodes.DATA_NO_CHANGE; + try { + LOG.debug("Begin get router rules of {} sync", routerRuleKey); + HttpResponse rawResponse = consulRawClient.makeGetRequest(routeRuleKey, recurseParam, 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 rule, 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 routing. + RoutingProto.Routing.Builder newRoutingBuilder = RoutingProto.Routing.newBuilder(); + newRoutingBuilder.setNamespace(StringValue.of(namespace)); + newRoutingBuilder.setService(StringValue.of(service)); + newRoutingBuilder.setRevision(StringValue.of(String.valueOf(newIndex))); + // create discover response. + ResponseProto.DiscoverResponse.Builder newDiscoverResponseBuilder = ResponseProto.DiscoverResponse.newBuilder(); + newDiscoverResponseBuilder.setService(newServiceBuilder); + // 重写index + List routes = new ArrayList<>(); + if (Objects.nonNull(newIndex)) { + if (!Objects.equals(currentIndex, newIndex)) { + code = ServerCodes.EXECUTE_SUCCESS; + if (rawResponse.getStatusCode() == 200) { + if (rawResponse.getContent() != null) { + LOG.info("new 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 Rule] Consul data is not changed"); + } + } else { + LOG.warn("[TSF Route Rule] Consul data is abnormal. {}", rawResponse); + } + if (CollectionUtils.isNotEmpty(routes)) { + newRoutingBuilder.addAllInbounds(routes); + } + newDiscoverResponseBuilder.setRouting(newRoutingBuilder); + newDiscoverResponseBuilder.setCode(UInt32Value.of(code)); + ServerEvent serverEvent = new ServerEvent(serviceUpdateTask.getServiceEventKey(), newDiscoverResponseBuilder.build(), null, SERVER_CONNECTOR_CONSUL); + boolean svcDeleted = serviceUpdateTask.notifyServerEvent(serverEvent); + if (newIndex != null) { + setRouterRuleConsulIndex(routerRuleKey, currentIndex, newIndex); + } + if (!svcDeleted) { + serviceUpdateTask.addUpdateTaskSet(); + } + } + } catch (Throwable e) { + LOG.error("[TSF Route Rule] tsf route rule load error. Will sleep for {} ms. Key path:{}", + consulContext.getConsulErrorSleep(), routeRuleKey, 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 routing 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 + Yaml yaml = new Yaml(); + ObjectMapper mapper = new ObjectMapper(); + // 配置 ObjectMapper在反序列化时,忽略目标对象没有的属性 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + List routeRuleGroupList = Lists.newArrayList(); + valueList.forEach(value -> { + try { + String routeJsonString = mapper + .writeValueAsString(yaml.load(value.getDecodedValue())); + List tempList = mapper.readValue(routeJsonString, + new TypeReference>() { + }); + if (!CollectionUtils.isEmpty(tempList)) { + routeRuleGroupList.add(tempList.get(0)); + } + } catch (Exception ex) { + LOG.error("tsf route rule load error.", ex); + throw new PolarisException(ErrorCode.INVALID_RESPONSE, "tsf route rule load error", ex); + } + }); + + // list -> List + List routes = Lists.newArrayList(); + for (RouteRuleGroup routeRuleGroup : routeRuleGroupList) { + for (RouteRule routeRule : routeRuleGroup.getRuleList()) { + RoutingProto.Route.Builder routeBuilder = RoutingProto.Route.newBuilder(); + routeBuilder.putExtendInfo(ROUTER_FAULT_TOLERANCE_ENABLE, String.valueOf(routeRuleGroup.getFallbackStatus())); + // parse sources + List sources = Lists.newArrayList(); + RoutingProto.Source.Builder sourceBuilder = RoutingProto.Source.newBuilder(); + sourceBuilder.setNamespace(StringValue.of("*")); + sourceBuilder.setService(StringValue.of("*")); + if (CollectionUtils.isNotEmpty(routeRule.getTagList())) { + for (RouteTag routeTag : routeRule.getTagList()) { + if (StringUtils.equals(routeTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_SERVICE_NAME)) { + sourceBuilder.setService(StringValue.of(routeTag.getTagValue())); + } else if (StringUtils.equals(routeTag.getTagField(), TagConstant.SYSTEM_FIELD.SOURCE_NAMESPACE_SERVICE_NAME)) { + String[] split = routeTag.getTagValue().split("/"); + if (split.length == 2) { + sourceBuilder.setNamespace(StringValue.of(split[0])); + sourceBuilder.setService(StringValue.of(split[1])); + } + } else { + ModelProto.MatchString.Builder matchStringBuilder = ModelProto.MatchString.newBuilder(); + matchStringBuilder.setType(parseMatchStringType(routeTag)); + matchStringBuilder.setValue(StringValue.of(routeTag.getTagValue())); + matchStringBuilder.setValueType(ModelProto.MatchString.ValueType.TEXT); + String metadataKey = routeTag.getTagField(); + sourceBuilder.putMetadata(parseMetadataKey(metadataKey), matchStringBuilder.build()); + } + } + } + sources.add(sourceBuilder.build()); + // parse destinations + List destinations = Lists.newArrayList(); + for (RouteDest routeDest : routeRule.getDestList()) { + RoutingProto.Destination.Builder destBuilder = RoutingProto.Destination.newBuilder(); + destBuilder.setNamespace(StringValue.of(namespace)); + destBuilder.setService(StringValue.of(service)); + destBuilder.setPriority(UInt32Value.of(0)); + destBuilder.setIsolate(BoolValue.of(false)); + destBuilder.setWeight(UInt32Value.of(routeDest.getDestWeight())); + destBuilder.setName(StringValue.of(routeDest.getDestId())); + for (RouteDestItem routeDestItem : routeDest.getDestItemList()) { + ModelProto.MatchString.Builder matchStringBuilder = ModelProto.MatchString.newBuilder(); + matchStringBuilder.setType(ModelProto.MatchString.MatchStringType.EXACT); + matchStringBuilder.setValue(StringValue.of(routeDestItem.getDestItemValue())); + matchStringBuilder.setValueType(ModelProto.MatchString.ValueType.TEXT); + destBuilder.putMetadata(routeDestItem.getDestItemField(), matchStringBuilder.build()); + } + destinations.add(destBuilder.build()); + } + routeBuilder.addAllSources(sources); + routeBuilder.addAllDestinations(destinations); + routes.add(routeBuilder.build()); + } + } + return routes; + } + + private Long getRouterRuleConsulIndex(RouterRuleKey routerRuleKey) { + Long index = routerRuleConsulIndexMap.get(routerRuleKey); + if (index != null) { + return index; + } + setRouterRuleConsulIndex(routerRuleKey, null, -1L); + return -1L; + } + + private void setRouterRuleConsulIndex(RouterRuleKey routerRuleKey, Long lastIndex, Long newIndex) { + LOG.debug("RouterRuleKey: {}; lastIndex: {}; newIndex: {}", routerRuleKey, lastIndex, newIndex); + routerRuleConsulIndexMap.put(routerRuleKey, newIndex); + } + + static class RouterRuleKey { + private String namespace = ""; + private String service = ""; + private Boolean fetchGroup = true; + + 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; + } + + public Boolean getFetchGroup() { + return fetchGroup; + } + + public void setFetchGroup(Boolean fetchGroup) { + this.fetchGroup = fetchGroup; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + RouterRuleKey that = (RouterRuleKey) object; + return Objects.equals(getNamespace(), that.getNamespace()) && Objects.equals(getService(), that.getService()) && Objects.equals(getFetchGroup(), that.getFetchGroup()); + } + + @Override + public int hashCode() { + return Objects.hash(getNamespace(), getService(), getFetchGroup()); + } + + @Override + public String toString() { + return "RouterRuleKey{" + + "namespace='" + namespace + '\'' + + ", serviceName='" + service + '\'' + + ", fetchGroup=" + fetchGroup + + '}'; + } + } +} 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-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDest.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDest.java new file mode 100644 index 000000000..bc6186060 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDest.java @@ -0,0 +1,108 @@ +/* + * 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.entity; + +import java.io.Serializable; +import java.util.List; + +/** + * TSF 路由规则目标实体 + * + * @author jingerzhang + */ + +public class RouteDest implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 984582541720418394L; + + /** + * 空构造函数 + */ + public RouteDest() { + } + + /** + * 路由规则项路由目标Id + */ + + private String destId; + + /** + * 路由目标权重 + */ + private Integer destWeight; + + /** + * 路由目标匹配条件列表 + *

+ * 忽略到数据库表字段的映射 + */ + + private List destItemList; + + /** + * 路由目标所属路由规则项Id + */ + private String routeRuleId; + + public String getDestId() { + return destId; + } + + public void setDestId(String destId) { + this.destId = destId; + } + + public Integer getDestWeight() { + return destWeight; + } + + public void setDestWeight(Integer destWeight) { + this.destWeight = destWeight; + } + + public List getDestItemList() { + return destItemList; + } + + public void setDestItemList(List destItemList) { + this.destItemList = destItemList; + } + + public String getRouteRuleId() { + return routeRuleId; + } + + public void setRouteRuleId(String routeRuleId) { + this.routeRuleId = routeRuleId; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("RouteDest{"); + sb.append("destId='").append(destId).append('\''); + sb.append(", destWeight=").append(destWeight); + sb.append(", destItemList=").append(destItemList); + sb.append(", routeRuleId='").append(routeRuleId).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDestItem.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDestItem.java new file mode 100644 index 000000000..966882885 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteDestItem.java @@ -0,0 +1,102 @@ +/* + * 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.entity; + +import java.io.Serializable; + +/** + * TSF 路由规则路由目标匹配项实体 + * + * @author jingerzhang + */ +public class RouteDestItem implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -2276599402100262473L; + + /** + * 空构造函数 + */ + public RouteDestItem() { + } + + /** + * 路由规则路由目标匹配项ID + */ + private String routeDestItemId; + + /** + * 所属路由规则路由目标ID + */ + private String routeDestId; + + /** + * 路由规则目标字段名称 + */ + private String destItemField; + + /** + * 路由规则目标字段取值 + */ + private String destItemValue; + + public String getDestItemField() { + return destItemField; + } + + public void setDestItemField(String destItemField) { + this.destItemField = destItemField; + } + + public String getDestItemValue() { + return destItemValue; + } + + public void setDestItemValue(String destItemValue) { + this.destItemValue = destItemValue; + } + + public String getRouteDestItemId() { + return routeDestItemId; + } + + public void setRouteDestItemId(String routeDestItemId) { + this.routeDestItemId = routeDestItemId; + } + + public String getRouteDestId() { + return routeDestId; + } + + public void setRouteDestId(String routeDestId) { + this.routeDestId = routeDestId; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("RouteDestItem{"); + sb.append("routeDestItemId='").append(routeDestItemId).append('\''); + sb.append(", routeDestId='").append(routeDestId).append('\''); + sb.append(", destItemField='").append(destItemField).append('\''); + sb.append(", destItemValue='").append(destItemValue).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRule.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRule.java new file mode 100644 index 000000000..1c0a4e67c --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRule.java @@ -0,0 +1,103 @@ +/* + * 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.entity; + +import java.io.Serializable; +import java.util.List; + +/** + * TSF 路由规则项实体 + * + * @author jingerzhang + */ +public class RouteRule implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -1886125299472426511L; + + /** + * 空构造函数 + */ + public RouteRule() { + } + + /** + * 路由规则项ID + */ + private String routeRuleId; + + /** + * 路由规则项所属路由规则ID + */ + private String routeId; + + /** + * 路由规则项包含的匹配条件列表 + */ + private List tagList; + + /** + * 路由规则项包含的目的列表 + */ + private List destList; + + public String getRouteRuleId() { + return routeRuleId; + } + + public void setRouteRuleId(String routeRuleId) { + this.routeRuleId = routeRuleId; + } + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public List getTagList() { + return tagList; + } + + public void setTagList(List tagList) { + this.tagList = tagList; + } + + public List getDestList() { + return destList; + } + + public void setDestList(List destList) { + this.destList = destList; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("RouteRule{"); + sb.append("routeRuleId='").append(routeRuleId).append('\''); + sb.append(", routeId='").append(routeId).append('\''); + sb.append(", tagList=").append(tagList); + sb.append(", destList=").append(destList); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRuleGroup.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRuleGroup.java new file mode 100644 index 000000000..8e8873fb5 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteRuleGroup.java @@ -0,0 +1,159 @@ +/* + * 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.entity; + +import java.io.Serializable; +import java.util.List; + +/** + * TSF 路由规则实体 + * + * @author jingerzhang + */ +public class RouteRuleGroup implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -3066353351857943148L; + + /** + * 空构造函数 + */ + public RouteRuleGroup() { + } + + /** + * 路由规则主键,全局唯一,前缀route + */ + private String routeId; + + /** + * 路由规则名称 + */ + private String routeName; + + /** + * 路由规则描述 + */ + private String routeDesc; + + /** + * 路由规则微服务ID + */ + private String microserviceId; + + /** + * TAG 路由规则详情 + */ + private List ruleList; + + /** + * microserviceId 微服务所属命名空间id + */ + private String namespaceId; + + /** + * microserviceId 微服务 服务名称 + */ + private String microserviceName; + + /** + * 是否开启路由规则保护策略 + */ + private Boolean fallbackStatus; + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getRouteName() { + return routeName; + } + + public void setRouteName(String routeName) { + this.routeName = routeName; + } + + public String getRouteDesc() { + return routeDesc; + } + + public void setRouteDesc(String routeDesc) { + this.routeDesc = routeDesc; + } + + public String getMicroserviceId() { + return microserviceId; + } + + public void setMicroserviceId(String microserviceId) { + this.microserviceId = microserviceId; + } + + public List getRuleList() { + return ruleList; + } + + public void setRuleList(List ruleList) { + this.ruleList = ruleList; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getMicroserviceName() { + return microserviceName; + } + + public void setMicroserviceName(String microserviceName) { + this.microserviceName = microserviceName; + } + + public Boolean getFallbackStatus() { + return fallbackStatus; + } + + public void setFallbackStatus(Boolean fallbackStatus) { + this.fallbackStatus = fallbackStatus; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("RouteRuleGroup{"); + sb.append("routeId='").append(routeId).append('\''); + sb.append(", routeName='").append(routeName).append('\''); + sb.append(", routeDesc='").append(routeDesc).append('\''); + sb.append(", microserviceId='").append(microserviceId).append('\''); + sb.append(", ruleList=").append(ruleList); + sb.append(", namespaceId='").append(namespaceId).append('\''); + sb.append(", microserviceName='").append(microserviceName).append('\''); + sb.append(", fallbackStatus=").append(fallbackStatus); + sb.append('}'); + return sb.toString(); + } +} diff --git a/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteTag.java b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteTag.java new file mode 100644 index 000000000..87a3b5c94 --- /dev/null +++ b/polaris-plugins/polaris-plugins-connector/connector-consul/src/main/java/com/tencent/polaris/plugins/connector/consul/service/router/entity/RouteTag.java @@ -0,0 +1,61 @@ +/* + * 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.entity; + + +import com.tencent.polaris.plugins.connector.consul.service.common.TagCondition; + +/** + * TSF 路由规则项匹配条件实体 + * + * @author jingerzhang + */ +public class RouteTag extends TagCondition { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 2327676288471562419L; + + /** + * 空构造函数 + */ + public RouteTag() { + } + + /** + * 匹配项所述路由规则项Id + */ + private String routeRuleId; + + public String getRouteRuleId() { + return routeRuleId; + } + + public void setRouteRuleId(String routeRuleId) { + this.routeRuleId = routeRuleId; + } + + + @Override + public String toString() { + return "RouteTag{" + + "routeRuleId='" + routeRuleId + '\'' + + "} " + super.toString(); + } +} 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/CircuitBreakCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java index 277ed819a..ade86d1d3 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java @@ -17,6 +17,7 @@ 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.EventType; @@ -41,7 +42,7 @@ protected String getRevision(DiscoverResponse discoverResponse) { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; CircuitBreaker circuitBreaker = discoverResponse.getCircuitBreaker(); String revision = ""; diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/FaultDetectCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/FaultDetectCacheHandler.java index f38b83e6e..13ad87c1b 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/FaultDetectCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/FaultDetectCacheHandler.java @@ -17,6 +17,7 @@ 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.EventType; @@ -41,7 +42,7 @@ public EventType getTargetEventType() { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; FaultDetector faultDetector = discoverResponse.getFaultDetector(); String revision = ""; diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/LaneRuleCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/LaneRuleCacheHandler.java index 178afd961..6c9e0095c 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/LaneRuleCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/LaneRuleCacheHandler.java @@ -17,15 +17,13 @@ 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.client.pojo.ServiceRuleByProto; 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.LaneProto; - -import java.util.List; public class LaneRuleCacheHandler extends AbstractCacheHandler { @Override @@ -40,7 +38,7 @@ public ServiceEventKey.EventType getTargetEventType() { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { ResponseProto.DiscoverResponse discoverResponse = (ResponseProto.DiscoverResponse) newValue; String revision = discoverResponse.getService().getRevision().getValue(); return new ServiceRuleByProto(discoverResponse, revision, isCacheLoaded, getTargetEventType()); 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/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java index 04cf5683f..1a0fef5c1 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java @@ -17,13 +17,21 @@ 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.EventType; +import com.tencent.polaris.api.utils.ApiTrieUtil; import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import com.tencent.polaris.specification.api.v1.model.ModelProto; import com.tencent.polaris.specification.api.v1.service.manage.ResponseProto.DiscoverResponse; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto.Routing; +import java.util.List; + +import static com.tencent.polaris.api.plugin.cache.CacheConstants.API_ID; + public class RoutingCacheHandler extends AbstractCacheHandler { @Override @@ -32,12 +40,35 @@ public EventType getTargetEventType() { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; Routing routing = discoverResponse.getRouting(); String revision = ""; if (null != routing) { revision = routing.getRevision().getValue(); + // 缓存 inbounds 中的 api 树 + List InboundsList = routing.getInboundsList(); + for (RoutingProto.Route route : InboundsList) { + List sources = route.getSourcesList(); + for (RoutingProto.Source source : sources) { + if (source.containsMetadata("$path")) { + ModelProto.MatchString matchString = source.getMetadataOrDefault("$path", ModelProto.MatchString.getDefaultInstance()); + if (matchString.getType() != ModelProto.MatchString.MatchStringType.REGEX) { + if (matchString.getType() == ModelProto.MatchString.MatchStringType.EXACT || matchString.getType() == ModelProto.MatchString.MatchStringType.NOT_EQUALS) { + flowCache.loadPluginCacheObject(API_ID, matchString.getValue().getValue(), + path -> ApiTrieUtil.buildSimpleTrieNode((String) path)); + } else if (matchString.getType() == ModelProto.MatchString.MatchStringType.IN || matchString.getType() == ModelProto.MatchString.MatchStringType.NOT_IN) { + String[] apis = matchString.getValue().getValue().split(","); + for (String api : apis) { + flowCache.loadPluginCacheObject(API_ID, api, + path -> ApiTrieUtil.buildSimpleTrieNode((String) path)); + } + } + } + } + } + } + } return new ServiceRuleByProto(routing, revision, isCacheLoaded, getTargetEventType()); } diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java index 239958333..45a46ce4d 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java @@ -17,6 +17,7 @@ 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.EventType; @@ -36,7 +37,7 @@ protected String getRevision(DiscoverResponse discoverResponse) { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; ServiceInstancesByProto oldServiceInstances = null; if (null != oldValue) { diff --git a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServicesCacheHandler.java b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServicesCacheHandler.java index e5cbe9a45..43ca033e5 100644 --- a/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServicesCacheHandler.java +++ b/polaris-plugins/polaris-plugins-connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServicesCacheHandler.java @@ -17,6 +17,7 @@ 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.EventType; @@ -37,7 +38,7 @@ protected String getRevision(DiscoverResponse discoverResponse) { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; return new ServicesByProto(discoverResponse, isCacheLoaded); } 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-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/CacheObject.java b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/CacheObject.java index 1cde82a77..20436a988 100644 --- a/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/CacheObject.java +++ b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/CacheObject.java @@ -102,7 +102,7 @@ public CacheObject(CacheHandler cacheHandler, ServiceEventKey svcEventKey, InMem long nowMs = System.currentTimeMillis(); createTime = nowMs; setLastAccessTimeMs(nowMs); - RegistryCacheValue registryCacheValue = cacheHandler.messageToCacheValue(null, initValue, true); + RegistryCacheValue registryCacheValue = cacheHandler.messageToCacheValue(null, initValue, true, registry.getFlowCache()); value.set(registryCacheValue); } @@ -214,7 +214,7 @@ public boolean onEventUpdate(ServerEvent event) { if (cachedStatus == CachedStatus.CacheChanged || cachedStatus == CachedStatus.CacheNotExists) { LOG.info("OnServiceUpdate: cache {} is pending to update", svcEventKey); this.registry.saveMessageToFile(serviceEventKey, (Message) message); - RegistryCacheValue newCachedValue = cacheHandler.messageToCacheValue(cachedValue, message, false); + RegistryCacheValue newCachedValue = cacheHandler.messageToCacheValue(cachedValue, message, false, registry.getFlowCache()); if (setValue(newCachedValue, event.getPolarisRevision()) && cachedStatus == CachedStatus.CacheChanged) { for (ResourceEventListener listener : resourceEventListeners) { listener.onResourceUpdated(svcEventKey, cachedValue, newCachedValue); diff --git a/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/InMemoryRegistry.java b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/InMemoryRegistry.java index 7d0fca09d..03af4dd78 100644 --- a/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/InMemoryRegistry.java +++ b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/InMemoryRegistry.java @@ -26,17 +26,12 @@ import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.plugin.Plugin; import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.cache.FlowCache; import com.tencent.polaris.api.plugin.common.InitContext; import com.tencent.polaris.api.plugin.common.PluginTypes; import com.tencent.polaris.api.plugin.compose.Extensions; import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; -import com.tencent.polaris.api.plugin.registry.CacheHandler; -import com.tencent.polaris.api.plugin.registry.EventCompleteNotifier; -import com.tencent.polaris.api.plugin.registry.InstanceProperty; -import com.tencent.polaris.api.plugin.registry.LocalRegistry; -import com.tencent.polaris.api.plugin.registry.ResourceEventListener; -import com.tencent.polaris.api.plugin.registry.ResourceFilter; -import com.tencent.polaris.api.plugin.registry.ServiceUpdateRequest; +import com.tencent.polaris.api.plugin.registry.*; import com.tencent.polaris.api.plugin.server.ServerConnector; import com.tencent.polaris.api.plugin.server.ServerEvent; import com.tencent.polaris.api.plugin.server.ServiceEventHandler; @@ -44,19 +39,8 @@ import com.tencent.polaris.api.plugin.stat.DefaultCircuitBreakResult; import com.tencent.polaris.api.plugin.stat.StatInfo; import com.tencent.polaris.api.plugin.stat.StatReporter; -import com.tencent.polaris.api.pojo.CircuitBreakerStatus; -import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; -import com.tencent.polaris.api.pojo.DetectResult; -import com.tencent.polaris.api.pojo.Instance; -import com.tencent.polaris.api.pojo.InstanceLocalValue; -import com.tencent.polaris.api.pojo.RegistryCacheValue; -import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.*; import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; -import com.tencent.polaris.api.pojo.ServiceInstances; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.pojo.ServiceRule; -import com.tencent.polaris.api.pojo.Services; -import com.tencent.polaris.api.pojo.StatusDimension; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.MapUtils; import com.tencent.polaris.api.utils.StringUtils; @@ -75,23 +59,9 @@ import org.slf4j.Logger; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; +import java.util.concurrent.*; /** * 本地缓存,保存服务端返回的实例信息. @@ -158,6 +128,11 @@ public class InMemoryRegistry extends Destroyable implements LocalRegistry { */ private boolean persistEnable; + /** + * 缓存是否超时淘汰 + */ + private boolean serviceExpireEnable; + /** * 缓存淘汰时间 */ @@ -168,6 +143,8 @@ public class InMemoryRegistry extends Destroyable implements LocalRegistry { */ private boolean hasDiscoverCluster = false; + private FlowCache flowCache; + private Collection statPlugins; @Override @@ -249,6 +226,15 @@ private ServerConnector getConnector() { return connector; } + /** + * 获取flow cache + * + * @return flow cache + */ + public FlowCache getFlowCache() { + return flowCache; + } + /** * 加载资源 * @@ -487,6 +473,7 @@ public void init(InitContext ctx) throws PolarisException { } NamedThreadFactory namedThreadFactory = new NamedThreadFactory(getName()); serviceExpireTimeMs = ctx.getConfig().getConsumer().getLocalCache().getServiceExpireTime(); + serviceExpireEnable = ctx.getConfig().getConsumer().getLocalCache().isServiceExpireEnable(); persistExecutor = Executors.newSingleThreadExecutor(namedThreadFactory); expireExecutor = Executors.newSingleThreadScheduledExecutor(namedThreadFactory); if (hasDiscoverCluster) { @@ -499,11 +486,14 @@ public void init(InitContext ctx) throws PolarisException { @Override public void postContextInit(Extensions extensions) throws PolarisException { - expireExecutor.scheduleAtFixedRate(new ExpireTask(), 0, serviceExpireTimeMs, TimeUnit.MILLISECONDS); + if (serviceExpireEnable) { + expireExecutor.scheduleAtFixedRate(new ExpireTask(), 0, serviceExpireTimeMs, TimeUnit.MILLISECONDS); + } if (null != serverServicesDiscoverExecutor) { serverServicesDiscoverExecutor.execute(new WarmupDiscoverServiceTask(extensions)); } statPlugins = extensions.getPlugins().getPlugins(PluginTypes.STAT_REPORTER.getBaseType()); + flowCache = extensions.getFlowCache(); } /** diff --git a/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/MessagePersistHandler.java b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/MessagePersistHandler.java index 79665926e..11f7f3450 100644 --- a/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/MessagePersistHandler.java +++ b/polaris-plugins/polaris-plugins-registry/registry-memory/src/main/java/com/tencent/polaris/plugins/registry/memory/MessagePersistHandler.java @@ -31,14 +31,7 @@ import org.slf4j.Logger; import org.yaml.snakeyaml.Yaml; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.RandomAccessFile; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.channels.FileChannel; @@ -225,8 +218,14 @@ private void doWriteTmpFile(File persistTmpFile, Message message) throws IOExcep String jsonStr = printer.usingTypeRegistry(registry).print(message); JsonNode jsonNodeTree = new ObjectMapper().readTree(jsonStr); String jsonAsYaml = new YAMLMapper().writeValueAsString(jsonNodeTree); + if (LOG.isDebugEnabled()) { + LOG.debug("start write file {} with content: {}.", persistTmpFile.getAbsolutePath(), jsonAsYaml); + } outputFile.write(jsonAsYaml.getBytes(StandardCharsets.UTF_8)); outputFile.flush(); + if (LOG.isDebugEnabled()) { + LOG.debug("write file {} with content: {} finished.", persistTmpFile.getAbsolutePath(), jsonAsYaml); + } } } @@ -282,7 +281,7 @@ public Long apply(ServiceEventKey serviceEventKey, Long aLong) { /** * 遍历缓存目录并加载之前缓存的服务信息 * - * @param eventKey 消息对象 + * @param eventKey 消息对象 * @param builderSupplier * @return 服务标识-消息对象的集合 */ 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-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java b/polaris-plugins/polaris-plugins-router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java index 4df32c9ff..a9aade17e 100644 --- a/polaris-plugins/polaris-plugins-router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java +++ b/polaris-plugins/polaris-plugins-router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java @@ -33,18 +33,16 @@ import com.tencent.polaris.api.rpc.MetadataFailoverType; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 正常场景:选出的实例子集不为空,那么优先返回健康子集,如果全部不健康则进行全死全活返回不健康子集。 - * + *

* 异常场景:需要根据GetOneInstanceRequest的请求策略进行降级决策 - * + *

* 不降级(默认):返回未找到实例错误 * 返回所有节点:优先返回服务下的健康子集,如果全部不健康则全死全活返回不健康子集 * 返回实例元数据不包含请求metadata的key的节点:优先返回筛选出的健康子集,如果全部不健康则返回不健康子集 @@ -60,6 +58,8 @@ public class MetadataRouter extends AbstractServiceRouter implements PluginConfi private static final String KEY_METADATA_FAILOVER_TYPE = "internal-metadata-failover-type"; + public static final String KEY_METADATA_KEYS = "metadataRouteKeys"; + private static final Map valueToFailoverType = new HashMap<>(); private static final Map inputToFailoverType = new HashMap<>(); @@ -188,6 +188,18 @@ private Map getRouterMetadata(RouteInfo routeInfo) { if (MapUtils.isNotEmpty(metadata)) { return metadata; } - return routeInfo.getRouterMetadata(ROUTER_TYPE_METADATA); + metadata = new HashMap<>(routeInfo.getRouterMetadata(ROUTER_TYPE_METADATA)); + if (routeInfo.getMetadataContainerGroup() != null && routeInfo.getMetadataContainerGroup().getCustomMetadataContainer() != null) { + String metadataRouteKeys = routeInfo.getMetadataContainerGroup().getCustomMetadataContainer().getRawMetadataMapValue(ROUTER_TYPE_METADATA, KEY_METADATA_KEYS); + if (StringUtils.isNotBlank(metadataRouteKeys)) { + String[] keysArr = metadataRouteKeys.split(","); + Set keysSet = new HashSet<>(Arrays.asList(keysArr)); + for (String key : keysSet) { + String value = routeInfo.getMetadataContainerGroup().getCustomMetadataContainer().getRawMetadataStringValue(key); + metadata.put(key, value); + } + } + } + return metadata; } } 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 e920e015f..38671cf95 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,8 +17,7 @@ package com.tencent.polaris.plugins.router.nearby; -import static com.tencent.polaris.client.util.Utils.isHealthyInstance; - +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; @@ -29,28 +28,28 @@ 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 java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +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 java.util.stream.Collectors; -import org.slf4j.Logger; +import static com.tencent.polaris.client.util.Utils.isHealthyInstance; /** * 就近接入路由 @@ -66,22 +65,23 @@ public class NearbyRouter extends AbstractServiceRouter implements PluginConfigP public static final String ROUTER_METADATA_KEY_REGION = "region"; public static final String ROUTER_METADATA_KEY_CAMPUS = "campus"; - private static final String NEARBY_METADATA_ENABLE = "internal-enable-nearby"; - + 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 # @@ -97,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 = 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) { + minAvailableLevel = nearbyRoutingConfig.getMatchLevel(); + } + RoutingProto.NearbyRoutingConfig.LocationLevel minLevel = minAvailableLevel; if (null != routeInfo.getNextRouterInfo()) { if (null != routeInfo.getNextRouterInfo().getLocationLevel()) { minLevel = routeInfo.getNextRouterInfo().getLocationLevel(); @@ -111,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) { + maxLevel = nearbyRoutingConfig.getMaxMatchLevel(); } - Map clientLocationInfo = getLocationInfo(); + Map clientLocationInfo = getLocationInfo(); if (minLevel.ordinal() >= maxLevel.ordinal()) { List instances = selectInstances(serviceInstances, minAvailableLevel, clientLocationInfo); if (CollectionUtils.isEmpty(instances)) { @@ -129,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; @@ -143,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; @@ -162,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)) { @@ -190,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)) { @@ -198,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)) { @@ -218,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); } @@ -293,6 +370,7 @@ public void init(InitContext ctx) throws PolarisException { */ @Override public void postContextInit(Extensions extensions) throws PolarisException { + this.extensions = extensions; //强制就近模式下,需要等待地域信息初始化成功 ensureLocationReady(); } @@ -315,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())); } @@ -325,7 +403,7 @@ private void refreshLocationInfo() { LOG.debug("[refreshLocationInfo] locationInfo={}", clientLocationInfo); } - private Map getLocationInfo() { + private Map getLocationInfo() { if (MapUtils.isEmpty(locationInfo.get())) { refreshLocationInfo(); } @@ -342,29 +420,72 @@ 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 = 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; } //默认关闭,需要显示打开 + boolean enabled = false; + if (routeInfo.getMetadataContainerGroup() != null && routeInfo.getMetadataContainerGroup().getCustomMetadataContainer() != null) { + String enabledStr = routeInfo.getMetadataContainerGroup().getCustomMetadataContainer().getRawMetadataMapValue(ROUTER_TYPE_NEAR_BY, ROUTER_ENABLED); + if (StringUtils.isNotBlank(enabledStr) && Boolean.parseBoolean(enabledStr)) { + enabled = true; + } + } Map routerMetadata = routeInfo.getRouterMetadata(ROUTER_TYPE_NEAR_BY); if (MapUtils.isNotEmpty(routerMetadata)) { - String enabled = routerMetadata.get(ROUTER_ENABLED); - return StringUtils.isNotBlank(enabled) && Boolean.parseBoolean(enabled); + String enabledStr = routerMetadata.get(ROUTER_ENABLED); + if (StringUtils.isNotBlank(enabledStr) && Boolean.parseBoolean(enabledStr)) { + enabled = true; + } } - return false; + return enabled; } @Override 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)) { @@ -399,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-plugins/polaris-plugins-router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java b/polaris-plugins/polaris-plugins-router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java index 5920e91ce..928db40a4 100644 --- a/polaris-plugins/polaris-plugins-router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java +++ b/polaris-plugins/polaris-plugins-router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java @@ -23,8 +23,10 @@ import com.tencent.polaris.api.exception.ErrorCode; import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.cache.FlowCache; import com.tencent.polaris.api.plugin.common.InitContext; import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; import com.tencent.polaris.api.plugin.route.RouteInfo; import com.tencent.polaris.api.plugin.route.RouteResult; import com.tencent.polaris.api.plugin.route.ServiceRouter; @@ -33,22 +35,19 @@ import com.tencent.polaris.api.pojo.ServiceInstances; import com.tencent.polaris.api.pojo.ServiceMetadata; import com.tencent.polaris.api.rpc.RuleBasedRouterFailoverType; -import com.tencent.polaris.api.utils.CollectionUtils; -import com.tencent.polaris.api.utils.MapUtils; -import com.tencent.polaris.api.utils.RuleUtils; -import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.api.utils.*; import com.tencent.polaris.logging.LoggerFactory; +import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup; import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto.Destination; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; import org.slf4j.Logger; +import java.util.*; + +import static com.tencent.polaris.api.plugin.cache.CacheConstants.API_ID; +import static com.tencent.polaris.api.plugin.route.RouterConstants.ROUTER_FAULT_TOLERANCE_ENABLE; + /** * 基于规则的服务路由能力 * @@ -65,6 +64,8 @@ public class RuleBasedRouter extends AbstractServiceRouter implements PluginConf private RuleBasedRouterConfig routerConfig; + private FlowCache flowCache; + /** * 根据路由规则进行服务实例过滤, 并返回过滤后的实例列表 * @@ -104,8 +105,9 @@ private List getRoutesFromRule(RouteInfo routeInfo, RuleMatc // 匹配source规则 private boolean matchSource(List sources, Service sourceService, - Map trafficLabels, - RuleMatchType ruleMatchType, Map multiEnvRouterParamMap) { + Map trafficLabels, + MetadataContainerGroup metadataContainerGroup, + RuleMatchType ruleMatchType, Map multiEnvRouterParamMap) { if (CollectionUtils.isEmpty(sources)) { return true; } @@ -151,8 +153,9 @@ private boolean matchSource(List sources, Service sourceSer continue; } - matched = RuleUtils.matchMetadata( - source.getMetadataMap(), trafficLabels, true, multiEnvRouterParamMap, globalVariablesConfig); + matched = RuleUtils.matchMetadata(source.getMetadataMap(), trafficLabels, metadataContainerGroup, true, + multiEnvRouterParamMap, globalVariablesConfig, + key -> flowCache.loadPluginCacheObject(API_ID, key, path -> ApiTrieUtil.buildSimpleTrieNode((String) path))); if (matched) { break; } @@ -176,7 +179,7 @@ private List filterAvailableDestinations(List getRuleFilteredInstances(RouteInfo routeInfo, ServiceInstances instances, - RuleMatchType ruleMatchType, MatchStatus matchStatus) throws PolarisException { + RuleMatchType ruleMatchType, MatchStatus matchStatus) throws PolarisException { // 获取路由规则 List routes = getRoutesFromRule(routeInfo, ruleMatchType); if (CollectionUtils.isEmpty(routes)) { @@ -186,6 +189,8 @@ private List getRuleFilteredInstances(RouteInfo routeInfo, ServiceInst for (RoutingProto.Route route : routes) { if (route == null) { continue; + } else { + matchStatus.fallback = Boolean.parseBoolean(route.getExtendInfoMap().get(ROUTER_FAULT_TOLERANCE_ENABLE)); } if (LOG.isDebugEnabled()) { @@ -193,10 +198,10 @@ private List getRuleFilteredInstances(RouteInfo routeInfo, ServiceInst } Map trafficLabels = routeInfo.getRouterMetadata(ROUTER_TYPE_RULE_BASED); + MetadataContainerGroup metadataContainerGroup = routeInfo.getMetadataContainerGroup(); // 匹配source规则 boolean sourceMatched = matchSource(route.getSourcesList(), routeInfo.getSourceService(), trafficLabels, - ruleMatchType, - multiEnvRouterParamMap); + metadataContainerGroup, ruleMatchType, multiEnvRouterParamMap); if (!sourceMatched) { continue; } @@ -248,8 +253,8 @@ private List getRuleFilteredInstances(RouteInfo routeInfo, ServiceInst /** * populateSubsetsFromDest 根据destination中的规则填充分组列表 * - * @param instances 实例信息 - * @param dest 目标规则 + * @param instances 实例信息 + * @param dest 目标规则 * @param subsetsMap 实例分组 * @return 是否成功加入subset列表 */ @@ -330,6 +335,7 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) { // 优先匹配inbound规则, 成功则不需要继续匹配outbound规则 List destFilteredInstances = null; List sourceFilteredInstances = null; + MatchStatus matchStatus = new MatchStatus(); if (routeInfo.getDestRouteRule() != null) { destFilteredInstances = getRuleFilteredInstances(routeInfo, instances, @@ -345,17 +351,20 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) { // 然后匹配outbound规则 sourceFilteredInstances = getRuleFilteredInstances(routeInfo, instances, RuleMatchType.sourceRouteRuleMatch, matchStatus); - if (sourceFilteredInstances.isEmpty()) { - ruleStatus = RuleStatus.sourceRuleFail; - } else { + if (!sourceFilteredInstances.isEmpty()) { ruleStatus = RuleStatus.sourceRuleSucc; } + if (sourceFilteredInstances.isEmpty() && matchStatus.matched) { + ruleStatus = RuleStatus.sourceRuleFail; + } } switch (ruleStatus) { case sourceRuleSucc: return new RouteResult(sourceFilteredInstances, RouteResult.State.Next); case destRuleSucc: return new RouteResult(destFilteredInstances, RouteResult.State.Next); + case noRule: + return new RouteResult(instances.getInstances(), RouteResult.State.Next); default: LOG.warn("route rule not match, rule status: {}, not matched source {}", ruleStatus, routeInfo.getSourceService()); @@ -366,7 +375,7 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) { failoverType = routerConfig.getFailoverType(); } - if (failoverType == RuleBasedRouterFailoverType.none) { + if (failoverType == RuleBasedRouterFailoverType.none && !matchStatus.fallback) { return new RouteResult(Collections.emptyList(), RouteResult.State.Next); } @@ -377,6 +386,8 @@ public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) { private static class MatchStatus { boolean matched; + + boolean fallback = false; } private List getHealthyInstances(List instances) { @@ -436,6 +447,11 @@ public void init(InitContext ctx) throws PolarisException { .getPluginConfig(getName(), RuleBasedRouterConfig.class); } + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + flowCache = extensions.getFlowCache(); + } + @Override public ServiceRouter.Aspect getAspect() { return ServiceRouter.Aspect.MIDDLE; @@ -452,13 +468,23 @@ public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { } //默认开启,需要显示关闭 + boolean enabled = true; + if (routeInfo.getMetadataContainerGroup() != null && routeInfo.getMetadataContainerGroup().getCustomMetadataContainer() != null) { + String enabledStr = routeInfo.getMetadataContainerGroup().getCustomMetadataContainer().getRawMetadataMapValue(ROUTER_TYPE_RULE_BASED, ROUTER_ENABLED); + if (StringUtils.isNotBlank(enabledStr) && !Boolean.parseBoolean(enabledStr)) { + enabled = false; + } + } Map routerMetadata = routeInfo.getRouterMetadata(ROUTER_TYPE_RULE_BASED); if (MapUtils.isNotEmpty(routerMetadata)) { - String enabled = routerMetadata.get(ROUTER_ENABLED); - if (StringUtils.isNotBlank(enabled) && !Boolean.parseBoolean(enabled)) { - return false; + String enabledStr = routerMetadata.get(ROUTER_ENABLED); + if (StringUtils.isNotBlank(enabledStr) && !Boolean.parseBoolean(enabledStr)) { + enabled = false; } } + if (!enabled) { + return false; + } List dstRoutes = getRoutesFromRule(routeInfo, RuleMatchType.destRouteRuleMatch); List srcRoutes = getRoutesFromRule(routeInfo, RuleMatchType.sourceRouteRuleMatch); diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/codec/RateLimitingCacheHandler.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/codec/RateLimitingCacheHandler.java index 1ecedd009..030572183 100644 --- a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/codec/RateLimitingCacheHandler.java +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/codec/RateLimitingCacheHandler.java @@ -18,6 +18,7 @@ package com.tencent.polaris.ratelimit.client.codec; import com.google.protobuf.StringValue; +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.EventType; @@ -32,6 +33,7 @@ import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto.MatchArgument.Type; import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto.RateLimit; import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto.Rule; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -54,7 +56,7 @@ protected String getRevision(DiscoverResponse discoverResponse) { } @Override - public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded, FlowCache flowCache) { DiscoverResponse discoverResponse = (DiscoverResponse) newValue; RateLimit rateLimit = discoverResponse.getRateLimit(); String revision = getRevision(discoverResponse); 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 352bda05d..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 @@ -17,230 +17,257 @@ package com.tencent.polaris.router.api.rpc; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; - import com.tencent.polaris.api.pojo.RouteArgument; import com.tencent.polaris.api.pojo.ServiceInfo; 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; import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.metadata.core.manager.MetadataContainerGroup; + +import java.util.*; +import java.util.function.Function; /** * 路由处理请求 */ public class ProcessRoutersRequest extends RequestBaseEntity { - private SourceService sourceService; + private SourceService sourceService; + + private RouterNamesGroup routers; + + private ServiceInstances dstInstances; + + private String method; + + //各个路由插件依赖的 metadata 参数 + private Map> routerArgument; - private RouterNamesGroup routers; + private MetadataContainerGroup metadataContainerGroup; - private ServiceInstances dstInstances; + //元数据路由降级策略 + private MetadataFailoverType metadataFailoverType; + // 规则路由降级策略 + private RuleBasedRouterFailoverType ruleBasedRouterFailoverType; + // 命名空间就近路由降级类型 + private NamespaceRouterFailoverType namespaceRouterFailoverType; - private String method; - - //各个路由插件依赖的 metadata 参数 - private Map> routerArgument; - //元数据路由降级策略 - private MetadataFailoverType metadataFailoverType; - - /** - * 北极星内部治理规则执行时,会识别规则中的参数来源类别,如果发现规则中的参数来源指定为外部数据源时,会调用本接口进行获取 - * - * 可以实现该接口,实现规则中的参数来源于配置中心、数据库、环境变量等等 - */ - private Function> externalParameterSupplier = s -> Optional.empty(); - - public Function> getExternalParameterSupplier() { - return externalParameterSupplier; - } - - public void setExternalParameterSupplier(Function> externalParameterSupplier) { - this.externalParameterSupplier = externalParameterSupplier; - } - - public ServiceInfo getSourceService() { - return sourceService; - } - - public void setSourceService(ServiceInfo serviceInfo) { - this.sourceService = new SourceService(); - this.sourceService.setService(serviceInfo.getService()); - this.sourceService.setNamespace(serviceInfo.getNamespace()); - - Optional.ofNullable(serviceInfo.getMetadata()).orElse(new HashMap<>()) - .forEach((key, value) -> sourceService.appendArguments(RouteArgument.fromLabel(key, value))); - - buildRouterArgumentsBySourceService(); - } - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public RouterNamesGroup getRouters() { - return routers; - } - - public void setRouters(RouterNamesGroup routers) { - this.routers = routers; - } - - public ServiceInstances getDstInstances() { - return dstInstances; - } - - public void setDstInstances(ServiceInstances dstInstances) { - this.dstInstances = dstInstances; - } - - public void putRouterArgument(String routerType, Set arguments) { - if (CollectionUtils.isEmpty(arguments)) { - return; - } - if (routerArgument == null) { - routerArgument = new HashMap<>(); - } - - routerArgument.put(routerType, arguments); - } + /** + * 北极星内部治理规则执行时,会识别规则中的参数来源类别,如果发现规则中的参数来源指定为外部数据源时,会调用本接口进行获取 + *

+ * 可以实现该接口,实现规则中的参数来源于配置中心、数据库、环境变量等等 + */ + private Function> externalParameterSupplier = s -> Optional.empty(); - public Set getRouterArguments(String routerType) { - buildRouterArgumentsBySourceService(); - Set arguments = routerArgument.get(routerType); - if (CollectionUtils.isEmpty(arguments)) { - return Collections.emptySet(); - } - - return Collections.unmodifiableSet(arguments); - } - - public Map> getRouterArguments() { - buildRouterArgumentsBySourceService(); - Map> routerArgument = new HashMap<>(this.routerArgument); - return Collections.unmodifiableMap(routerArgument); - } + public Function> getExternalParameterSupplier() { + return externalParameterSupplier; + } - public MetadataFailoverType getMetadataFailoverType() { - return metadataFailoverType; - } + public void setExternalParameterSupplier(Function> externalParameterSupplier) { + this.externalParameterSupplier = externalParameterSupplier; + } - public void setMetadataFailoverType(MetadataFailoverType metadataFailoverType) { - this.metadataFailoverType = metadataFailoverType; - } - - private void buildRouterArgumentsBySourceService() { - if (CollectionUtils.isEmpty(routerArgument)) { - routerArgument = new HashMap<>(); - } - if (Objects.isNull(sourceService)) { - return; - } - Set arguments = routerArgument.computeIfAbsent("ruleRouter", k -> new HashSet<>()); - arguments.addAll(sourceService.getArguments()); - } - - @Deprecated - public void putRouterMetadata(String routerType, Map metadata) { - if (MapUtils.isEmpty(metadata)) { - return; - } - if (routerArgument == null) { - routerArgument = new HashMap<>(); - } - - Set arguments = new HashSet<>(); - metadata.forEach((key, value) -> arguments.add(RouteArgument.fromLabel(key, value))); - - routerArgument.put(routerType, arguments); - } - - @Deprecated - public void addRouterMetadata(String routerType, Map metadata) { - if (MapUtils.isEmpty(metadata)) { - return; - } - - if (routerArgument == null) { - routerArgument = new HashMap<>(); - } - - Set arguments = routerArgument.computeIfAbsent(routerType, k -> new HashSet<>()); - metadata.forEach((key, value) -> arguments.add(RouteArgument.fromLabel(key, value))); - } - - @Deprecated - public Map getRouterMetadata(String routerType) { - buildRouterArgumentsBySourceService(); - Set arguments = routerArgument.get(routerType); - if (CollectionUtils.isEmpty(arguments)) { - return Collections.emptyMap(); - } - - Map metadata = new HashMap<>(); - arguments.forEach(argument -> argument.toLabel(metadata)); - - return Collections.unmodifiableMap(metadata); - } - - @Deprecated - public Map> getRouterMetadata() { - buildRouterArgumentsBySourceService(); - - Map> ret = new HashMap<>(); - - routerArgument.forEach((routerType, arguments) -> { - Map entry = ret.computeIfAbsent(routerType, k -> new HashMap<>()); - arguments.forEach(argument -> argument.toLabel(entry)); - }); - - return ret; - } - - public static class RouterNamesGroup { - - private List beforeRouters; - - private List coreRouters; - - private List afterRouters; - - public List getBeforeRouters() { - return beforeRouters; - } - - public void setBeforeRouters(List beforeRouters) { - this.beforeRouters = beforeRouters; - } - - public List getCoreRouters() { - return coreRouters; - } - - public void setCoreRouters(List coreRouters) { - this.coreRouters = coreRouters; - } - - public List getAfterRouters() { - return afterRouters; - } - - public void setAfterRouters(List afterRouters) { - this.afterRouters = afterRouters; - } - } + public ServiceInfo getSourceService() { + return sourceService; + } + + public void setSourceService(ServiceInfo serviceInfo) { + this.sourceService = new SourceService(); + this.sourceService.setService(serviceInfo.getService()); + this.sourceService.setNamespace(serviceInfo.getNamespace()); + + Optional.ofNullable(serviceInfo.getMetadata()).orElse(new HashMap<>()) + .forEach((key, value) -> sourceService.appendArguments(RouteArgument.fromLabel(key, value))); + + buildRouterArgumentsBySourceService(); + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public RouterNamesGroup getRouters() { + return routers; + } + + public void setRouters(RouterNamesGroup routers) { + this.routers = routers; + } + + public ServiceInstances getDstInstances() { + return dstInstances; + } + + public void setDstInstances(ServiceInstances dstInstances) { + this.dstInstances = dstInstances; + } + + public void putRouterArgument(String routerType, Set arguments) { + if (CollectionUtils.isEmpty(arguments)) { + return; + } + if (routerArgument == null) { + routerArgument = new HashMap<>(); + } + + routerArgument.put(routerType, arguments); + } + + public Set getRouterArguments(String routerType) { + buildRouterArgumentsBySourceService(); + Set arguments = routerArgument.get(routerType); + if (CollectionUtils.isEmpty(arguments)) { + return Collections.emptySet(); + } + + return Collections.unmodifiableSet(arguments); + } + + public Map> getRouterArguments() { + buildRouterArgumentsBySourceService(); + Map> routerArgument = new HashMap<>(this.routerArgument); + return Collections.unmodifiableMap(routerArgument); + } + + public MetadataFailoverType getMetadataFailoverType() { + return metadataFailoverType; + } + + public void setMetadataFailoverType(MetadataFailoverType metadataFailoverType) { + this.metadataFailoverType = metadataFailoverType; + } + + public RuleBasedRouterFailoverType getRuleBasedRouterFailoverType() { + return ruleBasedRouterFailoverType; + } + + public void setRuleBasedRouterFailoverType(RuleBasedRouterFailoverType ruleBasedRouterFailoverType) { + this.ruleBasedRouterFailoverType = ruleBasedRouterFailoverType; + } + + public MetadataContainerGroup getMetadataContainerGroup() { + return metadataContainerGroup; + } + + public void setMetadataContainerGroup(MetadataContainerGroup metadataContainerGroup) { + 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<>(); + } + if (Objects.isNull(sourceService)) { + return; + } + Set arguments = routerArgument.computeIfAbsent("ruleRouter", k -> new HashSet<>()); + arguments.addAll(sourceService.getArguments()); + } + + @Deprecated + public void putRouterMetadata(String routerType, Map metadata) { + if (MapUtils.isEmpty(metadata)) { + return; + } + if (routerArgument == null) { + routerArgument = new HashMap<>(); + } + + Set arguments = new HashSet<>(); + metadata.forEach((key, value) -> arguments.add(RouteArgument.fromLabel(key, value))); + + routerArgument.put(routerType, arguments); + } + + @Deprecated + public void addRouterMetadata(String routerType, Map metadata) { + if (MapUtils.isEmpty(metadata)) { + return; + } + + if (routerArgument == null) { + routerArgument = new HashMap<>(); + } + + Set arguments = routerArgument.computeIfAbsent(routerType, k -> new HashSet<>()); + metadata.forEach((key, value) -> arguments.add(RouteArgument.fromLabel(key, value))); + } + + @Deprecated + public Map getRouterMetadata(String routerType) { + buildRouterArgumentsBySourceService(); + Set arguments = routerArgument.get(routerType); + if (CollectionUtils.isEmpty(arguments)) { + return Collections.emptyMap(); + } + + Map metadata = new HashMap<>(); + arguments.forEach(argument -> argument.toLabel(metadata)); + + return Collections.unmodifiableMap(metadata); + } + + @Deprecated + public Map> getRouterMetadata() { + buildRouterArgumentsBySourceService(); + + Map> ret = new HashMap<>(); + + routerArgument.forEach((routerType, arguments) -> { + Map entry = ret.computeIfAbsent(routerType, k -> new HashMap<>()); + arguments.forEach(argument -> argument.toLabel(entry)); + }); + + return ret; + } + + public static class RouterNamesGroup { + + private List beforeRouters; + + private List coreRouters; + + private List afterRouters; + + public List getBeforeRouters() { + return beforeRouters; + } + + public void setBeforeRouters(List beforeRouters) { + this.beforeRouters = beforeRouters; + } + + public List getCoreRouters() { + return coreRouters; + } + + public void setCoreRouters(List coreRouters) { + this.coreRouters = coreRouters; + } + + public List getAfterRouters() { + return afterRouters; + } + + public void setAfterRouters(List afterRouters) { + this.afterRouters = afterRouters; + } + } } 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 844f22926..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 @@ -25,13 +25,8 @@ import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; import com.tencent.polaris.api.plugin.route.RouteInfo; import com.tencent.polaris.api.plugin.route.ServiceRouter; -import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; -import com.tencent.polaris.api.pojo.Instance; -import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.*; import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; -import com.tencent.polaris.api.pojo.ServiceInstances; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.pojo.SourceService; import com.tencent.polaris.api.utils.CollectionUtils; import com.tencent.polaris.api.utils.StringUtils; import com.tencent.polaris.client.api.SDKContext; @@ -45,10 +40,11 @@ import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest.RouterNamesGroup; import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; +import org.slf4j.Logger; + import java.util.HashSet; import java.util.List; import java.util.Set; -import org.slf4j.Logger; public class DefaultRouterFlow implements RouterFlow { @@ -103,6 +99,15 @@ public ProcessRoutersResponse processRouters(ProcessRoutersRequest request) { if (request.getMetadataFailoverType() != null) { routeInfo.setMetadataFailoverType(request.getMetadataFailoverType()); } + if (request.getRuleBasedRouterFailoverType() != null) { + routeInfo.setRuleBasedRouterFailoverType(request.getRuleBasedRouterFailoverType()); + } + 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