feat:merge router features from 1.5.2-Hoxton.SR9. (#232)
parent
fc4bd10564
commit
3a0f50af38
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* the context for router.
|
||||
*
|
||||
*@author lepdou 2022-05-17
|
||||
*/
|
||||
public class PolarisRouterContext {
|
||||
|
||||
/**
|
||||
* the label for rule router.
|
||||
*/
|
||||
public static final String RULE_ROUTER_LABELS = "ruleRouter";
|
||||
/**
|
||||
* transitive labels.
|
||||
*/
|
||||
public static final String TRANSITIVE_LABELS = "transitive";
|
||||
|
||||
private Map<String, Map<String, String>> labels;
|
||||
|
||||
public Map<String, String> getLabels(String labelType) {
|
||||
if (CollectionUtils.isEmpty(labels)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> subLabels = labels.get(labelType);
|
||||
if (CollectionUtils.isEmpty(subLabels)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return Collections.unmodifiableMap(subLabels);
|
||||
}
|
||||
|
||||
public void setLabels(String labelType, Map<String, String> subLabels) {
|
||||
if (this.labels == null) {
|
||||
this.labels = new HashMap<>();
|
||||
}
|
||||
labels.put(labelType, subLabels);
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.pojo.PolarisServiceInstance;
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import com.tencent.cloud.common.util.JacksonUtils;
|
||||
import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisNearByRouterProperties;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisRuleBasedRouterProperties;
|
||||
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerRequest;
|
||||
import com.tencent.polaris.api.exception.ErrorCode;
|
||||
import com.tencent.polaris.api.exception.PolarisException;
|
||||
import com.tencent.polaris.api.pojo.Instance;
|
||||
import com.tencent.polaris.api.pojo.ServiceInfo;
|
||||
import com.tencent.polaris.api.pojo.ServiceInstances;
|
||||
import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
|
||||
import com.tencent.polaris.plugins.router.nearby.NearbyRouter;
|
||||
import com.tencent.polaris.plugins.router.rule.RuleBasedRouter;
|
||||
import com.tencent.polaris.router.api.core.RouterAPI;
|
||||
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
|
||||
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
|
||||
import org.springframework.cloud.client.loadbalancer.Request;
|
||||
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
|
||||
import org.springframework.cloud.loadbalancer.core.DelegatingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Service routing entrance.
|
||||
*
|
||||
* Rule routing needs to rely on request parameters for server filtering.
|
||||
* The interface cannot obtain the context object of the request granularity,
|
||||
* so the routing capability cannot be achieved through ServerListFilter.
|
||||
*
|
||||
* And {@link PolarisRouterServiceInstanceListSupplier#get(Request)} provides the ability to pass in http headers,
|
||||
* so routing capabilities are implemented through IRule.
|
||||
*
|
||||
* @author Haotian Zhang, lepdou
|
||||
*/
|
||||
public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
|
||||
|
||||
private final PolarisNearByRouterProperties polarisNearByRouterProperties;
|
||||
private final PolarisMetadataRouterProperties polarisMetadataRouterProperties;
|
||||
private final PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties;
|
||||
private final RouterAPI routerAPI;
|
||||
|
||||
public PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
|
||||
RouterAPI routerAPI,
|
||||
PolarisNearByRouterProperties polarisNearByRouterProperties,
|
||||
PolarisMetadataRouterProperties polarisMetadataRouterProperties,
|
||||
PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties) {
|
||||
super(delegate);
|
||||
this.routerAPI = routerAPI;
|
||||
this.polarisNearByRouterProperties = polarisNearByRouterProperties;
|
||||
this.polarisMetadataRouterProperties = polarisMetadataRouterProperties;
|
||||
this.polarisRuleBasedRouterProperties = polarisRuleBasedRouterProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<List<ServiceInstance>> get() {
|
||||
throw new PolarisException(ErrorCode.INTERNAL_ERROR, "Unsupported method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<List<ServiceInstance>> get(Request request) {
|
||||
// 1. get all servers
|
||||
Flux<List<ServiceInstance>> allServers = getDelegate().get();
|
||||
|
||||
// 2. filter by router
|
||||
DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
|
||||
PolarisRouterContext key = null;
|
||||
if (requestContext instanceof RequestDataContext) {
|
||||
key = buildRouterContext(((RequestDataContext) requestContext).getClientRequest().getHeaders());
|
||||
}
|
||||
else if (requestContext.getClientRequest() instanceof PolarisLoadBalancerRequest) {
|
||||
key = buildRouterContext(((PolarisLoadBalancerRequest<?>) requestContext.getClientRequest()).getRequest()
|
||||
.getHeaders());
|
||||
}
|
||||
return doRouter(allServers, key);
|
||||
}
|
||||
|
||||
//set method to public for unit test
|
||||
PolarisRouterContext buildRouterContext(HttpHeaders headers) {
|
||||
Collection<String> labelHeaderValues = headers.get(RouterConstants.ROUTER_LABEL_HEADER);
|
||||
|
||||
if (CollectionUtils.isEmpty(labelHeaderValues)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||
|
||||
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get()
|
||||
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE));
|
||||
|
||||
labelHeaderValues.forEach(labelHeaderValue -> {
|
||||
Map<String, String> labels = JacksonUtils.deserialize2Map(labelHeaderValue);
|
||||
if (!CollectionUtils.isEmpty(labels)) {
|
||||
Map<String, String> unescapeLabels = new HashMap<>(labels.size());
|
||||
for (Map.Entry<String, String> entry : labels.entrySet()) {
|
||||
String escapedKey = ExpressionLabelUtils.unescape(entry.getKey());
|
||||
String escapedValue = ExpressionLabelUtils.unescape(entry.getValue());
|
||||
unescapeLabels.put(escapedKey, escapedValue);
|
||||
}
|
||||
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, unescapeLabels);
|
||||
}
|
||||
});
|
||||
|
||||
return routerContext;
|
||||
}
|
||||
|
||||
Flux<List<ServiceInstance>> doRouter(Flux<List<ServiceInstance>> allServers, PolarisRouterContext key) {
|
||||
ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(allServers);
|
||||
|
||||
// filter instance by routers
|
||||
ProcessRoutersRequest processRoutersRequest = buildProcessRoutersRequest(serviceInstances, key);
|
||||
|
||||
ProcessRoutersResponse processRoutersResponse = routerAPI.processRouters(processRoutersRequest);
|
||||
|
||||
List<ServiceInstance> filteredInstances = new ArrayList<>();
|
||||
ServiceInstances filteredServiceInstances = processRoutersResponse.getServiceInstances();
|
||||
for (Instance instance : filteredServiceInstances.getInstances()) {
|
||||
filteredInstances.add(new PolarisServiceInstance(instance));
|
||||
}
|
||||
return Flux.fromIterable(Collections.singletonList(filteredInstances));
|
||||
}
|
||||
|
||||
ProcessRoutersRequest buildProcessRoutersRequest(ServiceInstances serviceInstances, PolarisRouterContext key) {
|
||||
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
|
||||
processRoutersRequest.setDstInstances(serviceInstances);
|
||||
|
||||
// metadata router
|
||||
if (polarisMetadataRouterProperties.isEnabled()) {
|
||||
Map<String, String> transitiveLabels = getRouterLabels(key, PolarisRouterContext.TRANSITIVE_LABELS);
|
||||
processRoutersRequest.putRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA, transitiveLabels);
|
||||
}
|
||||
|
||||
// nearby router
|
||||
if (polarisNearByRouterProperties.isEnabled()) {
|
||||
Map<String, String> nearbyRouterMetadata = new HashMap<>();
|
||||
nearbyRouterMetadata.put(NearbyRouter.ROUTER_ENABLED, "true");
|
||||
processRoutersRequest.putRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY, nearbyRouterMetadata);
|
||||
}
|
||||
|
||||
// rule based router
|
||||
// set dynamic switch for rule based router
|
||||
boolean ruleBasedRouterEnabled = polarisRuleBasedRouterProperties.isEnabled();
|
||||
Map<String, String> ruleRouterMetadata = new HashMap<>();
|
||||
ruleRouterMetadata.put(RuleBasedRouter.ROUTER_ENABLED, String.valueOf(ruleBasedRouterEnabled));
|
||||
processRoutersRequest.putRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED, ruleRouterMetadata);
|
||||
|
||||
ServiceInfo serviceInfo = new ServiceInfo();
|
||||
serviceInfo.setNamespace(MetadataContext.LOCAL_NAMESPACE);
|
||||
serviceInfo.setService(MetadataContext.LOCAL_SERVICE);
|
||||
|
||||
if (ruleBasedRouterEnabled) {
|
||||
Map<String, String> ruleRouterLabels = getRouterLabels(key, PolarisRouterContext.RULE_ROUTER_LABELS);
|
||||
// The label information that the rule based routing depends on
|
||||
// is placed in the metadata of the source service for transmission.
|
||||
// Later, can consider putting it in routerMetadata like other routers.
|
||||
serviceInfo.setMetadata(ruleRouterLabels);
|
||||
}
|
||||
|
||||
processRoutersRequest.setSourceService(serviceInfo);
|
||||
|
||||
return processRoutersRequest;
|
||||
}
|
||||
|
||||
private Map<String, String> getRouterLabels(PolarisRouterContext key, String type) {
|
||||
if (key != null) {
|
||||
return key.getLabels(type);
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import com.tencent.cloud.polaris.context.ServiceRuleManager;
|
||||
import com.tencent.polaris.client.pb.ModelProto;
|
||||
import com.tencent.polaris.client.pb.RoutingProto;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Resolve label expressions from routing rules.
|
||||
* @author lepdou 2022-05-19
|
||||
*/
|
||||
public class RouterRuleLabelResolver {
|
||||
|
||||
private final ServiceRuleManager serviceRuleManager;
|
||||
|
||||
public RouterRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
|
||||
this.serviceRuleManager = serviceRuleManager;
|
||||
}
|
||||
|
||||
public Set<String> getExpressionLabelKeys(String namespace, String sourceService, String dstService) {
|
||||
List<RoutingProto.Route> rules = serviceRuleManager.getServiceRouterRule(namespace, sourceService, dstService);
|
||||
|
||||
if (CollectionUtils.isEmpty(rules)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<String> expressionLabels = new HashSet<>();
|
||||
|
||||
for (RoutingProto.Route rule : rules) {
|
||||
List<RoutingProto.Source> sources = rule.getSourcesList();
|
||||
if (CollectionUtils.isEmpty(sources)) {
|
||||
continue;
|
||||
}
|
||||
for (RoutingProto.Source source : sources) {
|
||||
Map<String, ModelProto.MatchString> labels = source.getMetadataMap();
|
||||
if (CollectionUtils.isEmpty(labels)) {
|
||||
continue;
|
||||
}
|
||||
for (String labelKey : labels.keySet()) {
|
||||
if (ExpressionLabelUtils.isExpressionLabel(labelKey)) {
|
||||
expressionLabels.add(labelKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expressionLabels;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.config;
|
||||
|
||||
import com.tencent.cloud.polaris.router.PolarisRouterServiceInstanceListSupplier;
|
||||
import com.tencent.polaris.router.api.core.RouterAPI;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Auto configuration for ribbon components.
|
||||
* @author lepdou 2022-05-17
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
public class LoadBalancerConfiguration {
|
||||
|
||||
/**
|
||||
* Order of reactive discovery service instance supplier.
|
||||
*/
|
||||
private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnReactiveDiscoveryEnabled
|
||||
@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER)
|
||||
static class PolarisReactiveSupportConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ReactiveDiscoveryClient.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "polaris")
|
||||
public ServiceInstanceListSupplier polarisRouterDiscoveryClientServiceInstanceListSupplier(
|
||||
ConfigurableApplicationContext context,
|
||||
RouterAPI routerAPI,
|
||||
PolarisNearByRouterProperties polarisNearByRouterProperties,
|
||||
PolarisMetadataRouterProperties polarisMetadataRouterProperties,
|
||||
PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties) {
|
||||
return new PolarisRouterServiceInstanceListSupplier(
|
||||
ServiceInstanceListSupplier.builder().withDiscoveryClient().build(context),
|
||||
routerAPI,
|
||||
polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties,
|
||||
polarisRuleBasedRouterProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBlockingDiscoveryEnabled
|
||||
@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
|
||||
static class PolarisBlockingSupportConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(DiscoveryClient.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "polaris")
|
||||
public ServiceInstanceListSupplier polarisRouterDiscoveryClientServiceInstanceListSupplier(
|
||||
ConfigurableApplicationContext context,
|
||||
RouterAPI routerAPI,
|
||||
PolarisNearByRouterProperties polarisNearByRouterProperties,
|
||||
PolarisMetadataRouterProperties polarisMetadataRouterProperties,
|
||||
PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties) {
|
||||
return new PolarisRouterServiceInstanceListSupplier(
|
||||
ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().build(context),
|
||||
routerAPI,
|
||||
polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties,
|
||||
polarisRuleBasedRouterProperties);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* the configuration for metadata router.
|
||||
* @author lepdou 2022-05-23
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cloud.polaris.router.metadata-router")
|
||||
public class PolarisMetadataRouterProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PolarisMetadataRouterProperties{" +
|
||||
"enabled=" + enabled +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* the configuration for nearby router.
|
||||
*
|
||||
* @author lepdou 2022-05-23
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cloud.polaris.router.nearby-router")
|
||||
public class PolarisNearByRouterProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PolarisNearByRouterProperties{" +
|
||||
"enabled=" + enabled +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* the configuration for rule based router.
|
||||
*
|
||||
* @author lepdou 2022-05-23
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cloud.polaris.router.rule-router")
|
||||
public class PolarisRuleBasedRouterProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PolarisNearByRouterProperties{" +
|
||||
"enabled=" + enabled +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.polaris.context.ServiceRuleManager;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
|
||||
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
|
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* router module auto configuration.
|
||||
*
|
||||
*@author lepdou 2022-05-11
|
||||
*/
|
||||
@Configuration
|
||||
@LoadBalancerClients(defaultConfiguration = LoadBalancerConfiguration.class)
|
||||
@Import({PolarisNearByRouterProperties.class, PolarisMetadataRouterProperties.class, PolarisRuleBasedRouterProperties.class})
|
||||
public class RouterAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List<RouterLabelResolver> routerLabelResolvers,
|
||||
MetadataLocalProperties metadataLocalProperties,
|
||||
RouterRuleLabelResolver routerRuleLabelResolver) {
|
||||
return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(HIGHEST_PRECEDENCE)
|
||||
public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() {
|
||||
return new PolarisLoadBalancerBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouterRuleLabelResolver routerRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
|
||||
return new RouterRuleLabelResolver(serviceRuleManager);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.feign;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import feign.RequestTemplate;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Resolve rule expression label from feign request.
|
||||
* @author lepdou 2022-05-20
|
||||
*/
|
||||
public class FeignExpressionLabelUtils {
|
||||
|
||||
public static Map<String, String> resolve(RequestTemplate request, Set<String> labelKeys) {
|
||||
if (CollectionUtils.isEmpty(labelKeys)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
|
||||
for (String labelKey : labelKeys) {
|
||||
if (StringUtils.startsWithIgnoreCase(labelKey, ExpressionLabelUtils.LABEL_HEADER_PREFIX)) {
|
||||
String headerKey = ExpressionLabelUtils.parseHeaderKey(labelKey);
|
||||
if (StringUtils.isBlank(headerKey)) {
|
||||
continue;
|
||||
}
|
||||
labels.put(labelKey, getHeaderValue(request, headerKey));
|
||||
}
|
||||
else if (StringUtils.startsWithIgnoreCase(labelKey, ExpressionLabelUtils.LABEL_QUERY_PREFIX)) {
|
||||
String queryKey = ExpressionLabelUtils.parseQueryKey(labelKey);
|
||||
if (StringUtils.isBlank(queryKey)) {
|
||||
continue;
|
||||
}
|
||||
labels.put(labelKey, getQueryValue(request, queryKey));
|
||||
}
|
||||
else if (StringUtils.equalsIgnoreCase(ExpressionLabelUtils.LABEL_METHOD, labelKey)) {
|
||||
labels.put(labelKey, request.method());
|
||||
}
|
||||
else if (StringUtils.equalsIgnoreCase(ExpressionLabelUtils.LABEL_URI, labelKey)) {
|
||||
URI uri = URI.create(request.request().url());
|
||||
labels.put(labelKey, uri.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
public static String getHeaderValue(RequestTemplate request, String key) {
|
||||
Map<String, Collection<String>> headers = request.headers();
|
||||
return ExpressionLabelUtils.getFirstValue(headers, key);
|
||||
|
||||
}
|
||||
|
||||
public static String getQueryValue(RequestTemplate request, String key) {
|
||||
return ExpressionLabelUtils.getFirstValue(request.queries(), key);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.feign;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import com.tencent.cloud.common.util.JacksonUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterConstants;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Resolver labels from request.
|
||||
*
|
||||
*@author lepdou 2022-05-12
|
||||
*/
|
||||
public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class);
|
||||
|
||||
private final List<RouterLabelResolver> routerLabelResolvers;
|
||||
private final MetadataLocalProperties metadataLocalProperties;
|
||||
private final RouterRuleLabelResolver routerRuleLabelResolver;
|
||||
|
||||
public RouterLabelFeignInterceptor(List<RouterLabelResolver> routerLabelResolvers,
|
||||
MetadataLocalProperties metadataLocalProperties,
|
||||
RouterRuleLabelResolver routerRuleLabelResolver) {
|
||||
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
|
||||
this.routerLabelResolvers = routerLabelResolvers;
|
||||
}
|
||||
else {
|
||||
this.routerLabelResolvers = null;
|
||||
}
|
||||
this.metadataLocalProperties = metadataLocalProperties;
|
||||
this.routerRuleLabelResolver = routerRuleLabelResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate requestTemplate) {
|
||||
// local service labels
|
||||
Map<String, String> labels = new HashMap<>(metadataLocalProperties.getContent());
|
||||
|
||||
// labels from rule expression
|
||||
String peerServiceName = requestTemplate.feignTarget().name();
|
||||
Map<String, String> ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, peerServiceName);
|
||||
labels.putAll(ruleExpressionLabels);
|
||||
|
||||
// labels from request
|
||||
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||
routerLabelResolvers.forEach(resolver -> {
|
||||
try {
|
||||
Map<String, String> customResolvedLabels = resolver.resolve(requestTemplate);
|
||||
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
|
||||
labels.putAll(customResolvedLabels);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// labels from downstream
|
||||
Map<String, String> transitiveLabels = MetadataContextHolder.get()
|
||||
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
|
||||
labels.putAll(transitiveLabels);
|
||||
|
||||
// Because when the label is placed in RequestTemplate.header,
|
||||
// RequestTemplate will parse the header according to the regular, which conflicts with the expression.
|
||||
// Avoid conflicts by escaping.
|
||||
Map<String, String> escapeLabels = new HashMap<>(labels.size());
|
||||
for (Map.Entry<String, String> entry : labels.entrySet()) {
|
||||
String escapedKey = ExpressionLabelUtils.escape(entry.getKey());
|
||||
String escapedValue = ExpressionLabelUtils.escape(entry.getValue());
|
||||
escapeLabels.put(escapedKey, escapedValue);
|
||||
}
|
||||
|
||||
// pass label by header
|
||||
if (escapeLabels.size() == 0) {
|
||||
requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER);
|
||||
return;
|
||||
}
|
||||
requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(escapeLabels));
|
||||
}
|
||||
|
||||
private Map<String, String> getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) {
|
||||
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
|
||||
MetadataContext.LOCAL_SERVICE, peerService);
|
||||
|
||||
if (CollectionUtils.isEmpty(labelKeys)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return FeignExpressionLabelUtils.resolve(requestTemplate, labelKeys);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.resttemplate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.BeanFactoryUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
|
||||
|
||||
/**
|
||||
* Replace LoadBalancerInterceptor with PolarisLoadBalancerInterceptor.
|
||||
* PolarisLoadBalancerInterceptor can pass routing context information.
|
||||
*
|
||||
*@author lepdou 2022-05-18
|
||||
*/
|
||||
public class PolarisLoadBalancerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
|
||||
|
||||
private BeanFactory factory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.factory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof LoadBalancerInterceptor) {
|
||||
LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class);
|
||||
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
|
||||
List<RouterLabelResolver> routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class);
|
||||
MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class);
|
||||
RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
|
||||
|
||||
return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory,
|
||||
routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.resttemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import com.tencent.cloud.common.util.JacksonUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterConstants;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor capabilities.
|
||||
* Parses the label from the request and puts it into the RouterContext for routing.
|
||||
*
|
||||
*@author lepdou 2022-05-18
|
||||
*/
|
||||
public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerInterceptor.class);
|
||||
|
||||
private final LoadBalancerClient loadBalancer;
|
||||
private final LoadBalancerRequestFactory requestFactory;
|
||||
private final List<RouterLabelResolver> routerLabelResolvers;
|
||||
private final MetadataLocalProperties metadataLocalProperties;
|
||||
private final RouterRuleLabelResolver routerRuleLabelResolver;
|
||||
|
||||
public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
|
||||
LoadBalancerRequestFactory requestFactory,
|
||||
List<RouterLabelResolver> routerLabelResolvers,
|
||||
MetadataLocalProperties metadataLocalProperties,
|
||||
RouterRuleLabelResolver routerRuleLabelResolver) {
|
||||
super(loadBalancer, requestFactory);
|
||||
this.loadBalancer = loadBalancer;
|
||||
this.requestFactory = requestFactory;
|
||||
this.metadataLocalProperties = metadataLocalProperties;
|
||||
this.routerRuleLabelResolver = routerRuleLabelResolver;
|
||||
|
||||
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
|
||||
this.routerLabelResolvers = routerLabelResolvers;
|
||||
}
|
||||
else {
|
||||
this.routerLabelResolvers = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
final URI originalUri = request.getURI();
|
||||
String peerServiceName = originalUri.getHost();
|
||||
Assert.state(peerServiceName != null,
|
||||
"Request URI does not contain a valid hostname: " + originalUri);
|
||||
|
||||
setLabelsToHeaders(request, body, peerServiceName);
|
||||
|
||||
return this.loadBalancer.execute(peerServiceName,
|
||||
new PolarisLoadBalancerRequest<>(request, this.requestFactory.createRequest(request, body, execution)));
|
||||
}
|
||||
|
||||
void setLabelsToHeaders(HttpRequest request, byte[] body, String peerServiceName) {
|
||||
// local service labels
|
||||
Map<String, String> labels = new HashMap<>(metadataLocalProperties.getContent());
|
||||
|
||||
// labels from rule expression
|
||||
Map<String, String> ruleExpressionLabels = getExpressionLabels(request, peerServiceName);
|
||||
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
|
||||
labels.putAll(ruleExpressionLabels);
|
||||
}
|
||||
|
||||
// labels from request
|
||||
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
|
||||
routerLabelResolvers.forEach(resolver -> {
|
||||
try {
|
||||
Map<String, String> customResolvedLabels = resolver.resolve(request, body);
|
||||
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
|
||||
labels.putAll(customResolvedLabels);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// labels from downstream
|
||||
Map<String, String> transitiveLabels = MetadataContextHolder.get()
|
||||
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
|
||||
labels.putAll(transitiveLabels);
|
||||
|
||||
// Because when the label is placed in RequestTemplate.header,
|
||||
// RequestTemplate will parse the header according to the regular, which conflicts with the expression.
|
||||
// Avoid conflicts by escaping.
|
||||
Map<String, String> escapeLabels = new HashMap<>(labels.size());
|
||||
for (Map.Entry<String, String> entry : labels.entrySet()) {
|
||||
String escapedKey = ExpressionLabelUtils.escape(entry.getKey());
|
||||
String escapedValue = ExpressionLabelUtils.escape(entry.getValue());
|
||||
escapeLabels.put(escapedKey, escapedValue);
|
||||
}
|
||||
|
||||
// pass label by header
|
||||
if (escapeLabels.size() == 0) {
|
||||
request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, null);
|
||||
return;
|
||||
}
|
||||
request.getHeaders().set(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(escapeLabels));
|
||||
}
|
||||
|
||||
private Map<String, String> getExpressionLabels(HttpRequest request, String peerServiceName) {
|
||||
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
|
||||
MetadataContext.LOCAL_SERVICE, peerServiceName);
|
||||
|
||||
if (CollectionUtils.isEmpty(labelKeys)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return ExpressionLabelUtils.resolve(request, labelKeys);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.resttemplate;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Wrapper of {@link LoadBalancerRequest}.
|
||||
*
|
||||
* @author Haotian Zhang
|
||||
*/
|
||||
public class PolarisLoadBalancerRequest<T> implements LoadBalancerRequest<T> {
|
||||
|
||||
private HttpRequest request;
|
||||
|
||||
private LoadBalancerRequest<T> delegate;
|
||||
|
||||
public PolarisLoadBalancerRequest(HttpRequest request, LoadBalancerRequest<T> delegate) {
|
||||
this.request = request;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T apply(ServiceInstance instance) throws Exception {
|
||||
return delegate.apply(instance);
|
||||
}
|
||||
|
||||
public HttpRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public LoadBalancerRequest<T> getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.spi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* The spi for resolving labels from request.
|
||||
*
|
||||
* @author lepdou 2022-05-11
|
||||
*/
|
||||
public interface RouterLabelResolver extends Ordered {
|
||||
|
||||
/**
|
||||
* resolve labels from feign request.
|
||||
* @param requestTemplate the feign request.
|
||||
* @return resolved labels
|
||||
*/
|
||||
Map<String, String> resolve(RequestTemplate requestTemplate);
|
||||
|
||||
/**
|
||||
* resolve labels from rest template request.
|
||||
* @param request the rest template request.
|
||||
* @param body the rest template request body.
|
||||
* @return resolved labels
|
||||
*/
|
||||
Map<String, String> resolve(HttpRequest request, byte[] body);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.cloud.polaris.router.metadata-router.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "the switch for metadata router."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.polaris.router.nearby-router.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "the switch for near by router."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.polaris.router.rule-router.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "the switch for rule based router."
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.tencent.cloud.polaris.router.config.RouterAutoConfiguration
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* test for {@link PolarisRouterContext}
|
||||
*
|
||||
*@author lepdou 2022-05-26
|
||||
*/
|
||||
public class PolarisRouterContextTest {
|
||||
|
||||
@Test
|
||||
public void testNormalGetterSetter() {
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
labels.put("k1", "v1");
|
||||
labels.put("k2", "v2");
|
||||
|
||||
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
|
||||
|
||||
Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
|
||||
Assert.assertEquals(2, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
|
||||
Assert.assertEquals("v1", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k1"));
|
||||
Assert.assertEquals("v2", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k2"));
|
||||
Assert.assertNull(routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNull() {
|
||||
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, null);
|
||||
Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
|
||||
Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEmptyRouterContext() {
|
||||
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||
Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size());
|
||||
Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size());
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.pojo.PolarisServiceInstance;
|
||||
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
|
||||
import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisNearByRouterProperties;
|
||||
import com.tencent.cloud.polaris.router.config.PolarisRuleBasedRouterProperties;
|
||||
import com.tencent.polaris.api.pojo.DefaultInstance;
|
||||
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
|
||||
import com.tencent.polaris.api.pojo.Instance;
|
||||
import com.tencent.polaris.api.pojo.ServiceInstances;
|
||||
import com.tencent.polaris.api.pojo.ServiceKey;
|
||||
import com.tencent.polaris.plugins.router.metadata.MetadataRouter;
|
||||
import com.tencent.polaris.plugins.router.nearby.NearbyRouter;
|
||||
import com.tencent.polaris.plugins.router.rule.RuleBasedRouter;
|
||||
import com.tencent.polaris.router.api.core.RouterAPI;
|
||||
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
|
||||
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* test for {@link PolarisRouterServiceInstanceListSupplier}
|
||||
*@author lepdou 2022-05-26
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PolarisRouterServiceInstanceListSupplierTest {
|
||||
|
||||
private static AtomicBoolean initTransitiveMetadata = new AtomicBoolean(false);
|
||||
@Mock
|
||||
private ServiceInstanceListSupplier delegate;
|
||||
@Mock
|
||||
private PolarisLoadBalancerProperties polarisLoadBalancerProperties;
|
||||
@Mock
|
||||
private PolarisNearByRouterProperties polarisNearByRouterProperties;
|
||||
@Mock
|
||||
private PolarisMetadataRouterProperties polarisMetadataRouterProperties;
|
||||
@Mock
|
||||
private PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties;
|
||||
@Mock
|
||||
private RouterAPI routerAPI;
|
||||
private String testNamespace = "testNamespace";
|
||||
private String testCallerService = "testCallerService";
|
||||
private String testCalleeService = "testCalleeService";
|
||||
|
||||
@Test
|
||||
public void testBuildMetadataRouteRequest() {
|
||||
when(polarisMetadataRouterProperties.isEnabled()).thenReturn(true);
|
||||
|
||||
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||
.thenReturn(testCallerService);
|
||||
|
||||
setTransitiveMetadata();
|
||||
|
||||
PolarisRouterServiceInstanceListSupplier compositeRule = new PolarisRouterServiceInstanceListSupplier(
|
||||
delegate, routerAPI, polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties, polarisRuleBasedRouterProperties);
|
||||
|
||||
ServiceInstances serviceInstances = assembleServiceInstances();
|
||||
PolarisRouterContext routerContext = assembleRouterContext();
|
||||
|
||||
ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
|
||||
|
||||
Map<String, String> routerMetadata = request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA);
|
||||
|
||||
Assert.assertEquals(1, routerMetadata.size());
|
||||
Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size());
|
||||
Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
|
||||
Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
|
||||
.get(RuleBasedRouter.ROUTER_ENABLED));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildNearbyRouteRequest() {
|
||||
when(polarisNearByRouterProperties.isEnabled()).thenReturn(true);
|
||||
|
||||
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||
.thenReturn(testCallerService);
|
||||
|
||||
setTransitiveMetadata();
|
||||
|
||||
PolarisRouterServiceInstanceListSupplier compositeRule = new PolarisRouterServiceInstanceListSupplier(
|
||||
delegate, routerAPI, polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties, polarisRuleBasedRouterProperties);
|
||||
|
||||
ServiceInstances serviceInstances = assembleServiceInstances();
|
||||
PolarisRouterContext routerContext = assembleRouterContext();
|
||||
|
||||
ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
|
||||
|
||||
Map<String, String> routerMetadata = request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY);
|
||||
|
||||
Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size());
|
||||
Assert.assertEquals(1, routerMetadata.size());
|
||||
Assert.assertEquals("true", routerMetadata.get(NearbyRouter.ROUTER_ENABLED));
|
||||
Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
|
||||
Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
|
||||
.get(RuleBasedRouter.ROUTER_ENABLED));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildRuleBasedRouteRequest() {
|
||||
when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true);
|
||||
|
||||
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).
|
||||
thenReturn(testCallerService);
|
||||
|
||||
setTransitiveMetadata();
|
||||
|
||||
PolarisRouterServiceInstanceListSupplier compositeRule = new PolarisRouterServiceInstanceListSupplier(
|
||||
delegate, routerAPI, polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties, polarisRuleBasedRouterProperties);
|
||||
|
||||
ServiceInstances serviceInstances = assembleServiceInstances();
|
||||
PolarisRouterContext routerContext = assembleRouterContext();
|
||||
|
||||
ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext);
|
||||
|
||||
Map<String, String> routerMetadata = request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED);
|
||||
|
||||
Assert.assertEquals(1, routerMetadata.size());
|
||||
Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size());
|
||||
Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size());
|
||||
Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size());
|
||||
Assert.assertEquals("true", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED)
|
||||
.get(RuleBasedRouter.ROUTER_ENABLED));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouter() {
|
||||
when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true);
|
||||
|
||||
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||
.thenReturn(testCallerService);
|
||||
|
||||
setTransitiveMetadata();
|
||||
|
||||
PolarisRouterServiceInstanceListSupplier compositeRule = new PolarisRouterServiceInstanceListSupplier(
|
||||
delegate, routerAPI, polarisNearByRouterProperties,
|
||||
polarisMetadataRouterProperties, polarisRuleBasedRouterProperties);
|
||||
|
||||
ProcessRoutersResponse assembleResponse = assembleProcessRoutersResponse();
|
||||
when(routerAPI.processRouters(any())).thenReturn(assembleResponse);
|
||||
|
||||
Flux<List<ServiceInstance>> servers = compositeRule.doRouter(assembleServers(), assembleRouterContext());
|
||||
|
||||
|
||||
Assert.assertEquals(assembleResponse.getServiceInstances().getInstances().size(),
|
||||
servers.toStream().mapToLong(List::size).sum());
|
||||
}
|
||||
}
|
||||
|
||||
private void setTransitiveMetadata() {
|
||||
if (initTransitiveMetadata.compareAndSet(false, true)) {
|
||||
// mock transitive metadata
|
||||
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
|
||||
try (MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
|
||||
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ServiceInstances assembleServiceInstances() {
|
||||
ServiceKey serviceKey = new ServiceKey(testNamespace, testCalleeService);
|
||||
List<Instance> instances = new LinkedList<>();
|
||||
instances.add(new DefaultInstance());
|
||||
instances.add(new DefaultInstance());
|
||||
instances.add(new DefaultInstance());
|
||||
instances.add(new DefaultInstance());
|
||||
instances.add(new DefaultInstance());
|
||||
|
||||
return new DefaultServiceInstances(serviceKey, instances);
|
||||
}
|
||||
|
||||
private PolarisRouterContext assembleRouterContext() {
|
||||
PolarisRouterContext routerContext = new PolarisRouterContext();
|
||||
Map<String, String> transitiveLabels = new HashMap<>();
|
||||
transitiveLabels.put("k1", "v1");
|
||||
Map<String, String> routerLabels = new HashMap<>();
|
||||
routerLabels.put("k2", "v2");
|
||||
routerLabels.put("k3", "v3");
|
||||
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
|
||||
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, routerLabels);
|
||||
return routerContext;
|
||||
}
|
||||
|
||||
private ProcessRoutersResponse assembleProcessRoutersResponse() {
|
||||
return new ProcessRoutersResponse(assembleServiceInstances());
|
||||
}
|
||||
|
||||
private Flux<List<ServiceInstance>> assembleServers() {
|
||||
ServiceInstances serviceInstances = assembleServiceInstances();
|
||||
List<ServiceInstance> servers = new ArrayList<>();
|
||||
for (Instance instance : serviceInstances.getInstances()) {
|
||||
servers.add(new PolarisServiceInstance(instance));
|
||||
}
|
||||
return Flux.fromIterable(Collections.singletonList(servers));
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.tencent.cloud.polaris.context.ServiceRuleManager;
|
||||
import com.tencent.polaris.client.pb.ModelProto;
|
||||
import com.tencent.polaris.client.pb.RoutingProto;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* test for {@link RouterRuleLabelResolver}
|
||||
*@author lepdou 2022-05-26
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RouterRuleLabelResolverTest {
|
||||
|
||||
@Mock
|
||||
private ServiceRuleManager serviceRuleManager;
|
||||
|
||||
private final String testNamespace = "testNamespace";
|
||||
private final String testSourceService = "sourceService";
|
||||
private final String testDstService = "dstService";
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Map<String, ModelProto.MatchString> labels = new HashMap<>();
|
||||
ModelProto.MatchString matchString = ModelProto.MatchString.getDefaultInstance();
|
||||
String validKey1 = "${http.header.uid}";
|
||||
String validKey2 = "${http.query.name}";
|
||||
String validKey3 = "${http.method}";
|
||||
String validKey4 = "${http.uri}";
|
||||
String invalidKey = "${http.expression.wrong}";
|
||||
labels.put(validKey1, matchString);
|
||||
labels.put(validKey2, matchString);
|
||||
labels.put(validKey3, matchString);
|
||||
labels.put(validKey4, matchString);
|
||||
labels.put(invalidKey, matchString);
|
||||
|
||||
RoutingProto.Source source1 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
|
||||
RoutingProto.Source source2 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build();
|
||||
RoutingProto.Source source3 = RoutingProto.Source.newBuilder().putAllMetadata(new HashMap<>()).build();
|
||||
|
||||
List<RoutingProto.Route> routes = new LinkedList<>();
|
||||
RoutingProto.Route route = RoutingProto.Route.newBuilder()
|
||||
.addAllSources(Lists.newArrayList(source1, source2, source3))
|
||||
.build();
|
||||
routes.add(route);
|
||||
|
||||
when(serviceRuleManager.getServiceRouterRule(testNamespace, testSourceService, testDstService)).thenReturn(routes);
|
||||
|
||||
RouterRuleLabelResolver resolver = new RouterRuleLabelResolver(serviceRuleManager);
|
||||
|
||||
Set<String> resolvedExpressionLabelKeys = resolver.getExpressionLabelKeys(testNamespace, testSourceService, testDstService);
|
||||
|
||||
Assert.assertNotNull(resolvedExpressionLabelKeys);
|
||||
Assert.assertEquals(4, resolvedExpressionLabelKeys.size());
|
||||
Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey1));
|
||||
Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey2));
|
||||
Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey3));
|
||||
Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey4));
|
||||
Assert.assertFalse(resolvedExpressionLabelKeys.contains(invalidKey));
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.feign;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Test for {@link FeignExpressionLabelUtils}
|
||||
*@author lepdou 2022-05-26
|
||||
*/
|
||||
public class FeignExpressionLabelUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testGetHeaderLabel() {
|
||||
String headerKey = "uid";
|
||||
String headerValue = "1000";
|
||||
String headerKey2 = "teacher.age";
|
||||
String headerValue2 = "1000";
|
||||
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.header(headerKey, headerValue);
|
||||
requestTemplate.header(headerKey2, headerValue2);
|
||||
|
||||
String labelKey1 = "${http.header.uid}";
|
||||
String labelKey2 = "${http.header.name}";
|
||||
String labelKey3 = "${http.headername}";
|
||||
String labelKey4 = "${http.header.}";
|
||||
String labelKey5 = "${http.header.teacher.age}";
|
||||
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
|
||||
Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5));
|
||||
|
||||
Assert.assertFalse(result.isEmpty());
|
||||
Assert.assertEquals(headerValue, result.get(labelKey1));
|
||||
Assert.assertEquals(headerValue2, result.get(labelKey5));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2)));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3)));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetQueryLabel() {
|
||||
String headerKey = "uid";
|
||||
String headerValue = "1000";
|
||||
String headerKey2 = "teacher.age";
|
||||
String headerValue2 = "1000";
|
||||
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.query(headerKey, headerValue);
|
||||
requestTemplate.query(headerKey2, headerValue2);
|
||||
|
||||
String labelKey1 = "${http.query.uid}";
|
||||
String labelKey2 = "${http.query.name}";
|
||||
String labelKey3 = "${http.queryname}";
|
||||
String labelKey4 = "${http.query.}";
|
||||
String labelKey5 = "${http.query.teacher.age}";
|
||||
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
|
||||
Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5));
|
||||
|
||||
Assert.assertFalse(result.isEmpty());
|
||||
Assert.assertEquals(headerValue, result.get(labelKey1));
|
||||
Assert.assertEquals(headerValue2, result.get(labelKey5));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2)));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3)));
|
||||
Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMethod() {
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.method(Request.HttpMethod.GET);
|
||||
|
||||
String labelKey1 = "${http.method}";
|
||||
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
|
||||
Sets.newHashSet(labelKey1));
|
||||
|
||||
Assert.assertFalse(result.isEmpty());
|
||||
Assert.assertEquals("GET", result.get(labelKey1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUri() {
|
||||
String uri = "/user/get";
|
||||
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.uri(uri);
|
||||
requestTemplate.method(Request.HttpMethod.GET);
|
||||
requestTemplate.target("http://localhost");
|
||||
requestTemplate = requestTemplate.resolve(new HashMap<>());
|
||||
|
||||
String labelKey1 = "${http.uri}";
|
||||
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
|
||||
Sets.newHashSet(labelKey1));
|
||||
|
||||
Assert.assertFalse(result.isEmpty());
|
||||
Assert.assertEquals(uri, result.get(labelKey1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUri2() {
|
||||
String uri = "/";
|
||||
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
requestTemplate.uri(uri);
|
||||
requestTemplate.method(Request.HttpMethod.GET);
|
||||
requestTemplate.target("http://localhost");
|
||||
requestTemplate = requestTemplate.resolve(new HashMap<>());
|
||||
|
||||
String labelKey1 = "${http.uri}";
|
||||
Map<String, String> result = FeignExpressionLabelUtils.resolve(requestTemplate,
|
||||
Sets.newHashSet(labelKey1));
|
||||
|
||||
Assert.assertFalse(result.isEmpty());
|
||||
Assert.assertEquals(uri, result.get(labelKey1));
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.feign;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
|
||||
import com.tencent.cloud.common.util.ExpressionLabelUtils;
|
||||
import com.tencent.cloud.common.util.JacksonUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterConstants;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Target;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* test for {@link RouterLabelFeignInterceptor}
|
||||
* @author lepdou 2022-05-26
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RouterLabelFeignInterceptorTest {
|
||||
|
||||
@Mock
|
||||
private MetadataLocalProperties metadataLocalProperties;
|
||||
@Mock
|
||||
private RouterRuleLabelResolver routerRuleLabelResolver;
|
||||
@Mock
|
||||
private RouterLabelResolver routerLabelResolver;
|
||||
|
||||
@Test
|
||||
public void testResolveRouterLabel() {
|
||||
RouterLabelFeignInterceptor routerLabelFeignInterceptor = new RouterLabelFeignInterceptor(
|
||||
Collections.singletonList(routerLabelResolver),
|
||||
metadataLocalProperties, routerRuleLabelResolver);
|
||||
|
||||
// mock request template
|
||||
RequestTemplate requestTemplate = new RequestTemplate();
|
||||
String headerUidKey = "uid";
|
||||
String headerUidValue = "1000";
|
||||
requestTemplate.header(headerUidKey, headerUidValue);
|
||||
String peerService = "peerService";
|
||||
Target.EmptyTarget<Object> target = Target.EmptyTarget.create(Object.class, peerService);
|
||||
requestTemplate.feignTarget(target);
|
||||
|
||||
// mock ApplicationContextAwareUtils#getProperties
|
||||
try (MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) {
|
||||
String testService = "callerService";
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||
.thenReturn(testService);
|
||||
|
||||
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
|
||||
|
||||
// mock transitive metadata
|
||||
Map<String, String> transitiveLabels = new HashMap<>();
|
||||
transitiveLabels.put("k1", "v1");
|
||||
transitiveLabels.put("k2", "v22");
|
||||
when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
|
||||
|
||||
// mock MetadataContextHolder#get
|
||||
try (MockedStatic<MetadataContextHolder> mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) {
|
||||
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
|
||||
|
||||
// mock custom resolved labels from request
|
||||
Map<String, String> customResolvedLabels = new HashMap<>();
|
||||
customResolvedLabels.put("k2", "v2");
|
||||
customResolvedLabels.put("k3", "v3");
|
||||
when(routerLabelResolver.resolve(requestTemplate)).thenReturn(customResolvedLabels);
|
||||
|
||||
// mock expression rule labels
|
||||
Set<String> expressionKeys = new HashSet<>();
|
||||
expressionKeys.add("${http.header.uid}");
|
||||
expressionKeys.add("${http.header.name}");
|
||||
when(routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
|
||||
MetadataContext.LOCAL_SERVICE, peerService)).thenReturn(expressionKeys);
|
||||
|
||||
// mock local metadata
|
||||
Map<String, String> localMetadata = new HashMap<>();
|
||||
localMetadata.put("k3", "v31");
|
||||
localMetadata.put("k4", "v4");
|
||||
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
|
||||
|
||||
routerLabelFeignInterceptor.apply(requestTemplate);
|
||||
|
||||
Collection<String> routerLabels = requestTemplate.headers().get(RouterConstants.ROUTER_LABEL_HEADER);
|
||||
|
||||
Assert.assertNotNull(routerLabels);
|
||||
for (String value : routerLabels) {
|
||||
Map<String, String> labels = unescape(JacksonUtils.deserialize2Map(value));
|
||||
|
||||
Assert.assertEquals("v1", labels.get("k1"));
|
||||
Assert.assertEquals("v22", labels.get("k2"));
|
||||
Assert.assertEquals("v3", labels.get("k3"));
|
||||
Assert.assertEquals("v4", labels.get("k4"));
|
||||
Assert.assertEquals(headerUidValue, labels.get("${http.header.uid}"));
|
||||
Assert.assertEquals("", labels.get("${http.header.name}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> unescape(Map<String, String> labels) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : labels.entrySet()) {
|
||||
result.put(ExpressionLabelUtils.unescape(entry.getKey()), ExpressionLabelUtils.unescape(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.resttemplate;
|
||||
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.BeanFactoryUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test for ${@link PolarisLoadBalancerBeanPostProcessor}
|
||||
* @author lepdou 2022-05-26
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PolarisLoadBalancerBeanPostProcessorTest {
|
||||
|
||||
@Mock
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
@Mock
|
||||
private LoadBalancerRequestFactory loadBalancerRequestFactory;
|
||||
@Mock
|
||||
private MetadataLocalProperties metadataLocalProperties;
|
||||
@Mock
|
||||
private RouterRuleLabelResolver routerRuleLabelResolver;
|
||||
@Mock
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Test
|
||||
public void testWrapperLoadBalancerInterceptor() {
|
||||
when(beanFactory.getBean(LoadBalancerRequestFactory.class)).thenReturn(loadBalancerRequestFactory);
|
||||
when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient);
|
||||
when(beanFactory.getBean(MetadataLocalProperties.class)).thenReturn(metadataLocalProperties);
|
||||
when(beanFactory.getBean(RouterRuleLabelResolver.class)).thenReturn(routerRuleLabelResolver);
|
||||
|
||||
try (MockedStatic<BeanFactoryUtils> mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) {
|
||||
mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, RouterLabelResolver.class))
|
||||
.thenReturn(null);
|
||||
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(loadBalancerClient, loadBalancerRequestFactory);
|
||||
|
||||
PolarisLoadBalancerBeanPostProcessor processor = new PolarisLoadBalancerBeanPostProcessor();
|
||||
processor.setBeanFactory(beanFactory);
|
||||
|
||||
Object bean = processor.postProcessBeforeInitialization(loadBalancerInterceptor, "");
|
||||
|
||||
Assert.assertTrue(bean instanceof PolarisLoadBalancerInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotWrapperLoadBalancerInterceptor() {
|
||||
PolarisLoadBalancerBeanPostProcessor processor = new PolarisLoadBalancerBeanPostProcessor();
|
||||
processor.setBeanFactory(beanFactory);
|
||||
|
||||
OtherBean otherBean = new OtherBean();
|
||||
Object bean = processor.postProcessBeforeInitialization(otherBean, "");
|
||||
Assert.assertFalse(bean instanceof PolarisLoadBalancerInterceptor);
|
||||
Assert.assertTrue(bean instanceof OtherBean);
|
||||
}
|
||||
|
||||
static class OtherBean {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.resttemplate;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
|
||||
import com.tencent.cloud.common.util.JacksonUtils;
|
||||
import com.tencent.cloud.polaris.router.RouterConstants;
|
||||
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* test for {@link PolarisLoadBalancerInterceptor}
|
||||
* @author lepdou 2022-05-26
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PolarisLoadBalancerInterceptorTest {
|
||||
|
||||
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
|
||||
private static MockedStatic<MetadataContextHolder> mockedMetadataContextHolder;
|
||||
@Mock
|
||||
private LoadBalancerClient loadBalancerClient;
|
||||
@Mock
|
||||
private LoadBalancerRequestFactory loadBalancerRequestFactory;
|
||||
@Mock
|
||||
private RouterLabelResolver routerLabelResolver;
|
||||
@Mock
|
||||
private MetadataLocalProperties metadataLocalProperties;
|
||||
@Mock
|
||||
private RouterRuleLabelResolver routerRuleLabelResolver;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
|
||||
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
|
||||
.thenReturn("callerService");
|
||||
|
||||
mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
mockedApplicationContextAwareUtils.close();
|
||||
mockedMetadataContextHolder.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxyRibbonLoadBalance() throws Exception {
|
||||
String callerService = "callerService";
|
||||
String calleeService = "calleeService";
|
||||
HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
|
||||
|
||||
// mock local metadata
|
||||
Map<String, String> localMetadata = new HashMap<>();
|
||||
localMetadata.put("k1", "v1");
|
||||
localMetadata.put("k2", "v2");
|
||||
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
|
||||
|
||||
// mock custom resolved from request
|
||||
Map<String, String> customResolvedLabels = new HashMap<>();
|
||||
customResolvedLabels.put("k3", "v3");
|
||||
customResolvedLabels.put("k4", "v4");
|
||||
when(routerLabelResolver.resolve(request, null)).thenReturn(customResolvedLabels);
|
||||
|
||||
// mock expression rule labels
|
||||
|
||||
Set<String> expressionKeys = new HashSet<>();
|
||||
expressionKeys.add("${http.method}");
|
||||
expressionKeys.add("${http.uri}");
|
||||
when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys);
|
||||
|
||||
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
|
||||
|
||||
// mock transitive metadata
|
||||
Map<String, String> transitiveLabels = new HashMap<>();
|
||||
transitiveLabels.put("k1", "v1");
|
||||
transitiveLabels.put("k2", "v22");
|
||||
when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
|
||||
|
||||
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
|
||||
|
||||
LoadBalancerRequest<ClientHttpResponse> loadBalancerRequest = new MockedLoadBalancerRequest<>();
|
||||
when(loadBalancerRequestFactory.createRequest(request, null, null)).thenReturn(loadBalancerRequest);
|
||||
|
||||
PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient,
|
||||
loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), metadataLocalProperties, routerRuleLabelResolver);
|
||||
|
||||
polarisLoadBalancerInterceptor.intercept(request, null, null);
|
||||
|
||||
verify(metadataLocalProperties).getContent();
|
||||
verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService);
|
||||
verify(routerLabelResolver).resolve(request, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouterContext() throws Exception {
|
||||
String callerService = "callerService";
|
||||
String calleeService = "calleeService";
|
||||
HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get");
|
||||
|
||||
// mock local metadata
|
||||
Map<String, String> localMetadata = new HashMap<>();
|
||||
localMetadata.put("k1", "v1");
|
||||
localMetadata.put("k2", "v2");
|
||||
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
|
||||
|
||||
// mock custom resolved from request
|
||||
Map<String, String> customResolvedLabels = new HashMap<>();
|
||||
customResolvedLabels.put("k2", "v22");
|
||||
customResolvedLabels.put("k4", "v4");
|
||||
when(routerLabelResolver.resolve(request, null)).thenReturn(customResolvedLabels);
|
||||
|
||||
// mock expression rule labels
|
||||
|
||||
Set<String> expressionKeys = new HashSet<>();
|
||||
expressionKeys.add("${http.method}");
|
||||
expressionKeys.add("${http.uri}");
|
||||
when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys);
|
||||
|
||||
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
|
||||
|
||||
// mock transitive metadata
|
||||
Map<String, String> transitiveLabels = new HashMap<>();
|
||||
transitiveLabels.put("k1", "v1");
|
||||
transitiveLabels.put("k2", "v22");
|
||||
when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
|
||||
|
||||
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
|
||||
|
||||
PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient,
|
||||
loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), metadataLocalProperties, routerRuleLabelResolver);
|
||||
|
||||
polarisLoadBalancerInterceptor.setLabelsToHeaders(request, null, calleeService);
|
||||
|
||||
verify(metadataLocalProperties).getContent();
|
||||
verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService);
|
||||
verify(routerLabelResolver).resolve(request, null);
|
||||
|
||||
Map<String, String> headers = JacksonUtils.deserialize2Map(request.getHeaders()
|
||||
.get(RouterConstants.ROUTER_LABEL_HEADER).get(0));
|
||||
Assert.assertEquals("v1", headers.get("k1"));
|
||||
Assert.assertEquals("v22", headers.get("k2"));
|
||||
Assert.assertEquals("v4", headers.get("k4"));
|
||||
Assert.assertEquals("GET", headers.get("##@$@##http.method}"));
|
||||
Assert.assertEquals("/user/get", headers.get("##@$@##http.uri}"));
|
||||
}
|
||||
|
||||
static class MockedLoadBalancerRequest<T> implements LoadBalancerRequest<T> {
|
||||
|
||||
@Override
|
||||
public T apply(ServiceInstance instance) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class MockedHttpRequest implements HttpRequest {
|
||||
|
||||
private URI uri;
|
||||
|
||||
private HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
MockedHttpRequest(String url) {
|
||||
this.uri = URI.create(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodValue() {
|
||||
return HttpMethod.GET.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.example;
|
||||
|
||||
/**
|
||||
* demo object.
|
||||
* @author lepdou 2022-05-12
|
||||
*/
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"name='" + name + '\'' +
|
||||
", age=" + age +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.example;
|
||||
|
||||
/**
|
||||
* demo object.
|
||||
* @author lepdou 2022-05-12
|
||||
*/
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"name='" + name + '\'' +
|
||||
", age=" + age +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.example;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
*
|
||||
* Customize the business tag information obtained from the request
|
||||
*
|
||||
*@author lepdou 2022-05-12
|
||||
*/
|
||||
@Component
|
||||
public class CustomRouterLabelResolver implements RouterLabelResolver {
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
@Override
|
||||
public Map<String, String> resolve(RequestTemplate requestTemplate) {
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
|
||||
User user = gson.fromJson(new String(requestTemplate.body()), User.class);
|
||||
|
||||
labels.put("user", user.getName());
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> resolve(HttpRequest request, byte[] body) {
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
User user = gson.fromJson(new String(body), User.class);
|
||||
|
||||
labels.put("user", user.getName());
|
||||
return labels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.example;
|
||||
|
||||
/**
|
||||
* demo object.
|
||||
* @author lepdou 2022-05-12
|
||||
*/
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-tencent-examples</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>polaris-router-grayrelease-example</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Tencent Polaris Router GrayRelease Example</name>
|
||||
<description>Example of Spring Cloud Tencent Polaris Router GrayRelease</description>
|
||||
<modules>
|
||||
<module>router-grayrelease-gateway</module>
|
||||
<module>router-grayrelease-frontend</module>
|
||||
<module>router-grayrelease-middle</module>
|
||||
<module>router-grayrelease-backend</module>
|
||||
</modules>
|
||||
</project>
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<parent>
|
||||
<artifactId>polaris-router-grayrelease-example</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>router-grayrelease-backend</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,15 @@
|
||||
############################################################
|
||||
# Dockerfile to build polaris-java quickstart example provider
|
||||
|
||||
# 1. You need to build the binary from the source code,
|
||||
# use `mvn clean install` to build the binary.
|
||||
# 2. You need to copy the quickstart-example-provider-*.jar to this directory
|
||||
# 3. Replace the ${VERSION} to the real version of the project
|
||||
|
||||
############################################################
|
||||
|
||||
FROM java:8
|
||||
|
||||
ADD router-grayrelease-backend-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar
|
||||
|
||||
ENTRYPOINT ["java","-jar","/root/app.jar"]
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.back;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/router/gray")
|
||||
public class BackController {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Get information of callee.
|
||||
* @return information of callee
|
||||
*/
|
||||
@GetMapping("/rest")
|
||||
public String rest() {
|
||||
String env = System.getenv("SCT_METADATA_CONTENT_env");
|
||||
String appName = environment.getProperty("spring.application.name");
|
||||
return appName + "[" + env + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.back;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class GrayReleaseBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GrayReleaseBackendApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
session-timeout: 1800
|
||||
port: 59002
|
||||
spring:
|
||||
application:
|
||||
name: gray-release-back
|
||||
cloud:
|
||||
polaris:
|
||||
address: grpc://183.47.111.80:8091
|
||||
namespace: default
|
||||
enabled: true
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: info
|
||||
com.tencent.cloud.polaris: debug
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<parent>
|
||||
<artifactId>polaris-router-grayrelease-example</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>router-grayrelease-frontend</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,15 @@
|
||||
############################################################
|
||||
# Dockerfile to build polaris-java quickstart example provider
|
||||
|
||||
# 1. You need to build the binary from the source code,
|
||||
# use `mvn clean install` to build the binary.
|
||||
# 2. You need to copy the quickstart-example-provider-*.jar to this directory
|
||||
# 3. Replace the ${VERSION} to the real version of the project
|
||||
|
||||
############################################################
|
||||
|
||||
FROM java:8
|
||||
|
||||
ADD router-grayrelease-frontend-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar
|
||||
|
||||
ENTRYPOINT ["java","-jar","/root/app.jar"]
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.front;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/router/gray")
|
||||
public class FrontController {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private RouterService routerService;
|
||||
|
||||
/**
|
||||
* Get information of callee.
|
||||
* @return information of callee
|
||||
*/
|
||||
@GetMapping("/rest")
|
||||
public String rest() {
|
||||
String env = System.getenv("SCT_METADATA_CONTENT_env");
|
||||
String appName = environment.getProperty("spring.application.name");
|
||||
String curName = appName + "[" + env + "]";
|
||||
String resp = routerService.rest();
|
||||
return curName + " -> " + resp;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.front;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class GrayReleaseFrontApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GrayReleaseFrontApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.front;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Router callee feign client.
|
||||
*
|
||||
* @author lepdou 2022-04-06
|
||||
*/
|
||||
@FeignClient("gray-release-middle")
|
||||
public interface RouterService {
|
||||
|
||||
@GetMapping("/router/gray/rest")
|
||||
String rest();
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
session-timeout: 1800
|
||||
port: 59000
|
||||
spring:
|
||||
application:
|
||||
name: gray-release-front
|
||||
cloud:
|
||||
polaris:
|
||||
address: grpc://183.47.111.80:8091
|
||||
namespace: default
|
||||
enabled: true
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: info
|
||||
com.tencent.cloud.polaris: debug
|
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<parent>
|
||||
<artifactId>polaris-router-grayrelease-example</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>router-grayrelease-gateway</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,15 @@
|
||||
############################################################
|
||||
# Dockerfile to build polaris-java quickstart example provider
|
||||
|
||||
# 1. You need to build the binary from the source code,
|
||||
# use `mvn clean install` to build the binary.
|
||||
# 2. You need to copy the quickstart-example-provider-*.jar to this directory
|
||||
# 3. Replace the ${VERSION} to the real version of the project
|
||||
|
||||
############################################################
|
||||
|
||||
FROM java:8
|
||||
|
||||
ADD router-grayrelease-gateway-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar
|
||||
|
||||
ENTRYPOINT ["java","-jar","/root/app.jar"]
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.gateway;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/router/gray")
|
||||
public class GatewayController {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private RouterService routerService;
|
||||
|
||||
/**
|
||||
* Get information of callee.
|
||||
* @return information of callee
|
||||
*/
|
||||
@GetMapping("/route_rule")
|
||||
public String routeRule(@RequestHeader("uid") int userId) {
|
||||
String appName = environment.getProperty("spring.application.name");
|
||||
String resp = routerService.restByUser(userId);
|
||||
return appName + " -> " + resp;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class GrayReleaseGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GrayReleaseGatewayApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.gateway;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
/**
|
||||
* Router callee feign client.
|
||||
*
|
||||
* @author lepdou 2022-04-06
|
||||
*/
|
||||
@FeignClient("gray-release-front")
|
||||
public interface RouterService {
|
||||
|
||||
@GetMapping("/router/gray/rest")
|
||||
String restByUser(@RequestHeader("uid") int user);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
session-timeout: 1800
|
||||
port: 59100
|
||||
spring:
|
||||
application:
|
||||
name: gray-release-gateway
|
||||
cloud:
|
||||
polaris:
|
||||
address: grpc://183.47.111.80:8091
|
||||
namespace: default
|
||||
enabled: true
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: info
|
||||
com.tencent.cloud.polaris: debug
|
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<parent>
|
||||
<artifactId>polaris-router-grayrelease-example</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>router-grayrelease-middle</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencent.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,15 @@
|
||||
############################################################
|
||||
# Dockerfile to build polaris-java quickstart example provider
|
||||
|
||||
# 1. You need to build the binary from the source code,
|
||||
# use `mvn clean install` to build the binary.
|
||||
# 2. You need to copy the quickstart-example-provider-*.jar to this directory
|
||||
# 3. Replace the ${VERSION} to the real version of the project
|
||||
|
||||
############################################################
|
||||
|
||||
FROM java:8
|
||||
|
||||
ADD router-grayrelease-middle-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar
|
||||
|
||||
ENTRYPOINT ["java","-jar","/root/app.jar"]
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.middle;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class GrayReleaseMiddleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GrayReleaseMiddleApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.middle;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/router/gray")
|
||||
public class MiddleController {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private RouterService routerService;
|
||||
|
||||
/**
|
||||
* Get information of callee.
|
||||
* @return information of callee
|
||||
*/
|
||||
@GetMapping("/rest")
|
||||
public String rest() {
|
||||
String env = System.getenv("SCT_METADATA_CONTENT_env");
|
||||
String appName = environment.getProperty("spring.application.name");
|
||||
String curName = appName + "[" + env + "]";
|
||||
String resp = routerService.rest();
|
||||
return curName + " -> " + resp;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.router.grayrelease.middle;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Router callee feign client.
|
||||
*
|
||||
* @author lepdou 2022-04-06
|
||||
*/
|
||||
@FeignClient("gray-release-back")
|
||||
public interface RouterService {
|
||||
|
||||
@GetMapping("/router/gray/rest")
|
||||
String rest();
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
session-timeout: 1800
|
||||
port: 59001
|
||||
spring:
|
||||
application:
|
||||
name: gray-release-middle
|
||||
cloud:
|
||||
polaris:
|
||||
address: grpc://183.47.111.80:8091
|
||||
namespace: default
|
||||
enabled: true
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: info
|
||||
com.tencent.cloud.polaris: debug
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making Spring Cloud Tencent 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.cloud.polaris.loadbalancer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.polaris.api.pojo.DefaultInstance;
|
||||
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
|
||||
import com.tencent.polaris.api.pojo.Instance;
|
||||
import com.tencent.polaris.api.pojo.ServiceInstances;
|
||||
import com.tencent.polaris.api.pojo.ServiceKey;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
/**
|
||||
* load balancer utils.
|
||||
*
|
||||
*@author lepdou 2022-05-17
|
||||
*/
|
||||
public class LoadBalancerUtils {
|
||||
|
||||
public static ServiceInstances transferServersToServiceInstances(Flux<List<ServiceInstance>> servers) {
|
||||
List<Instance> instances = servers.toStream().flatMap(List::stream).map(serviceInstance -> {
|
||||
DefaultInstance instance = new DefaultInstance();
|
||||
instance.setNamespace(MetadataContext.LOCAL_NAMESPACE);
|
||||
instance.setService(serviceInstance.getServiceId());
|
||||
instance.setProtocol(serviceInstance.getScheme());
|
||||
instance.setId(serviceInstance.getInstanceId());
|
||||
instance.setHost(serviceInstance.getHost());
|
||||
instance.setPort(serviceInstance.getPort());
|
||||
instance.setWeight(100);
|
||||
instance.setMetadata(serviceInstance.getMetadata());
|
||||
return instance;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
String serviceName = null;
|
||||
if (CollectionUtils.isNotEmpty(instances)) {
|
||||
serviceName = instances.get(0).getService();
|
||||
}
|
||||
|
||||
ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName);
|
||||
|
||||
return new DefaultServiceInstances(serviceKey, instances);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue