diff --git a/CHANGELOG.md b/CHANGELOG.md index dfddc6ef0..7b3f17d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Change Log --- -- [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183) +- [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183) +- [fix:Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165) \ No newline at end of file diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java index 711d5b947..2e6e78d24 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java @@ -65,14 +65,13 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Map headerMetadataMap = JacksonUtils .deserialize2Map(headerMetadataStr); for (String key : headerMetadataMap.keySet()) { - metadataContext.putTransitiveCustomMetadata(key, - headerMetadataMap.get(key)); + metadataContext.putContext(MetadataContext.FRAGMENT_TRANSITIVE, key, headerMetadataMap.get(key)); } } } - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + if (!CollectionUtils.isEmpty(customMetadata)) { String metadataStr = JacksonUtils.serialize2Json(customMetadata); requestTemplate.removeHeader(CUSTOM_METADATA); diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java index 47709ba6c..c97ddb9d8 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java @@ -63,12 +63,10 @@ public class EncodeTransferMedataRestTemplateInterceptor Map headerMetadataMap = JacksonUtils .deserialize2Map(metadataStr); for (String key : headerMetadataMap.keySet()) { - metadataContext.putTransitiveCustomMetadata(key, - headerMetadataMap.get(key)); + metadataContext.putContext(MetadataContext.FRAGMENT_TRANSITIVE, key, headerMetadataMap.get(key)); } } - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (!CollectionUtils.isEmpty(customMetadata)) { metadataStr = JacksonUtils.serialize2Json(customMetadata); try { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java index 6c5937bce..200a83743 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java @@ -65,8 +65,7 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered { if (metadataContext == null) { metadataContext = MetadataContextHolder.get(); } - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (!CollectionUtils.isEmpty(customMetadata)) { String metadataStr = JacksonUtils.serialize2Json(customMetadata); try { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java index 3fac6f6d7..cf192e955 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java @@ -65,8 +65,7 @@ public class EncodeTransferMetadataZuulFilter extends ZuulFilter { MetadataContext metadataContext = MetadataContextHolder.get(); // add new metadata and cover old - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (!CollectionUtils.isEmpty(customMetadata)) { String metadataStr = JacksonUtils.serialize2Json(customMetadata); try { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java index d71e93a64..7d7811420 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java @@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import com.tencent.cloud.common.constant.MetadataConstant; +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.metadata.core.EncodeTransferMedataFeignInterceptor; @@ -52,8 +53,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = DEFINED_PORT, classes = EncodeTransferMedataFeignInterceptorTest.TestApplication.class, - properties = { "server.port=8081", - "spring.config.location = classpath:application-test.yml" }) + properties = {"server.port=8081", + "spring.config.location = classpath:application-test.yml"}) public class EncodeTransferMedataFeignInterceptorTest { @Autowired @@ -72,13 +73,13 @@ public class EncodeTransferMedataFeignInterceptorTest { Assertions.assertThat(metadataLocalProperties.getContent().get("b")) .isEqualTo("2"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("a")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a")) .isEqualTo("11"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("b")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b")) .isEqualTo("22"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("c")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c")) .isEqualTo("33"); } @@ -98,8 +99,8 @@ public class EncodeTransferMedataFeignInterceptorTest { public interface TestFeign { @RequestMapping(value = "/test", - headers = { MetadataConstant.HeaderName.CUSTOM_METADATA - + "={\"a\":\"11" + "\",\"b\":\"22\",\"c\":\"33\"}" }) + headers = {MetadataConstant.HeaderName.CUSTOM_METADATA + + "={\"a\":\"11" + "\",\"b\":\"22\",\"c\":\"33\"}"}) String test(); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java index 733539df4..26f1bc395 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java @@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import com.tencent.cloud.common.constant.MetadataConstant; +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.metadata.core.EncodeTransferMedataRestTemplateInterceptor; @@ -82,13 +83,13 @@ public class EncodeTransferMedataRestTemplateInterceptorTest { Assertions.assertThat(metadataLocalProperties.getContent().get("b")) .isEqualTo("2"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("a")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a")) .isEqualTo("11"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("b")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b")) .isEqualTo("22"); Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("c")) + .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c")) .isEqualTo("33"); } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java index 0996f0b21..54ab9a53e 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java @@ -13,6 +13,7 @@ * 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.circuitbreaker.feign; diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java index e71eb1fcd..bd64f463b 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java @@ -71,8 +71,8 @@ public class PolarisDiscoveryHandler { getInstancesRequest.setService(service); String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localService = MetadataContext.LOCAL_SERVICE; - Map allTransitiveCustomMetadata = MetadataContextHolder.get() - .getAllTransitiveCustomMetadata(); + Map allTransitiveCustomMetadata = MetadataContextHolder.get(). + getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (StringUtils.isNotBlank(localNamespace) || StringUtils.isNotBlank(localService) || null != allTransitiveCustomMetadata) { ServiceInfo sourceService = new ServiceInfo(); diff --git a/spring-cloud-starter-tencent-polaris-router/pom.xml b/spring-cloud-starter-tencent-polaris-router/pom.xml index d623da7b1..94a69d9bc 100644 --- a/spring-cloud-starter-tencent-polaris-router/pom.xml +++ b/spring-cloud-starter-tencent-polaris-router/pom.xml @@ -27,6 +27,12 @@ router-rule + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java new file mode 100644 index 000000000..d270f766c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java @@ -0,0 +1,184 @@ +/* + * 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.List; +import java.util.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractLoadBalancerRule; +import com.netflix.loadbalancer.AvailabilityFilteringRule; +import com.netflix.loadbalancer.BestAvailableRule; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.RandomRule; +import com.netflix.loadbalancer.RetryRule; +import com.netflix.loadbalancer.RoundRobinRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.WeightedResponseTimeRule; +import com.netflix.loadbalancer.ZoneAvoidanceRule; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServer; +import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils; +import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +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.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; +import org.apache.commons.lang.StringUtils; + +import org.springframework.util.CollectionUtils; + +/** + * + * Service routing entrance. + * + * Rule routing needs to rely on request parameters for server filtering, + * and {@link com.netflix.loadbalancer.ServerListFilter#getFilteredListOfServers(List)} + * The interface cannot obtain the context object of the request granularity, + * so the routing capability cannot be achieved through ServerListFilter. + * + * And {@link com.netflix.loadbalancer.IRule#choose(Object)} provides the ability to pass in context parameters, + * so routing capabilities are implemented through IRule. + * + * @author Haotian Zhang, lepdou + */ +public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule { + + private final static String STRATEGY_RANDOM = "random"; + private final static String STRATEGY_ROUND_ROBIN = "roundRobin"; + private final static String STRATEGY_WEIGHT = "polarisWeighted"; + private final static String STRATEGY_RETRY = "retry"; + private final static String STRATEGY_RESPONSE_TIME_WEIGHTED = "responseTimeWeighted"; + private final static String STRATEGY_BEST_AVAILABLE = "bestAvailable"; + private final static String STRATEGY_ZONE_AVOIDANCE = "zoneAvoidance"; + private final static String STRATEGY_AVAILABILITY_FILTERING = "availabilityFilteringRule"; + + private final PolarisLoadBalancerProperties loadBalancerProperties; + private final RouterAPI routerAPI; + + private final AbstractLoadBalancerRule delegateRule; + + public PolarisLoadBalancerCompositeRule(RouterAPI routerAPI, PolarisLoadBalancerProperties polarisLoadBalancerProperties, + IClientConfig iClientConfig) { + this.routerAPI = routerAPI; + this.loadBalancerProperties = polarisLoadBalancerProperties; + + delegateRule = getRule(); + delegateRule.initWithNiwsConfig(iClientConfig); + } + + @Override + public void initWithNiwsConfig(IClientConfig clientConfig) { + } + + @Override + public Server choose(Object key) { + // 1. get all servers + List allServers = getLoadBalancer().getReachableServers(); + if (CollectionUtils.isEmpty(allServers)) { + return null; + } + + // 2. filter by router + List serversAfterRouter = doRouter(allServers, key); + + // 3. filter by load balance. + // A LoadBalancer needs to be regenerated for each request, + // because the list of servers may be different after filtered by router + ILoadBalancer loadBalancer = new SimpleLoadBalancer(); + loadBalancer.addServers(serversAfterRouter); + delegateRule.setLoadBalancer(loadBalancer); + + return delegateRule.choose(key); + } + + private List doRouter(List allServers, Object key) { + ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(allServers); + + // filter instance by routers + ProcessRoutersRequest processRoutersRequest = buildProcessRoutersRequest(serviceInstances, key); + + ProcessRoutersResponse processRoutersResponse = routerAPI.processRouters(processRoutersRequest); + + List filteredInstances = new ArrayList<>(); + ServiceInstances filteredServiceInstances = processRoutersResponse.getServiceInstances(); + for (Instance instance : filteredServiceInstances.getInstances()) { + filteredInstances.add(new PolarisServer(serviceInstances, instance)); + } + return filteredInstances; + } + + private ProcessRoutersRequest buildProcessRoutersRequest(ServiceInstances serviceInstances, Object key) { + ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); + processRoutersRequest.setDstInstances(serviceInstances); + + Map routerMetadata; + if (key instanceof PolarisRouterContext) { + routerMetadata = ((PolarisRouterContext) key).getLabels(); + } + else { + routerMetadata = Collections.emptyMap(); + } + + String srcNamespace = MetadataContext.LOCAL_NAMESPACE; + String srcService = MetadataContext.LOCAL_SERVICE; + + if (StringUtils.isNotBlank(srcNamespace) && StringUtils.isNotBlank(srcService)) { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setNamespace(srcNamespace); + serviceInfo.setService(srcService); + serviceInfo.setMetadata(routerMetadata); + processRoutersRequest.setSourceService(serviceInfo); + } + + return processRoutersRequest; + } + + public AbstractLoadBalancerRule getRule() { + String loadBalanceStrategy = loadBalancerProperties.getStrategy(); + if (org.springframework.util.StringUtils.isEmpty(loadBalanceStrategy)) { + return new RoundRobinRule(); + } + + switch (loadBalanceStrategy) { + case STRATEGY_RANDOM: + return new RandomRule(); + case STRATEGY_WEIGHT: + return new PolarisWeightedRule(routerAPI); + case STRATEGY_RETRY: + return new RetryRule(); + case STRATEGY_RESPONSE_TIME_WEIGHTED: + return new WeightedResponseTimeRule(); + case STRATEGY_BEST_AVAILABLE: + return new BestAvailableRule(); + case STRATEGY_ROUND_ROBIN: + return new RoundRobinRule(); + case STRATEGY_AVAILABILITY_FILTERING: + return new AvailabilityFilteringRule(); + case STRATEGY_ZONE_AVOIDANCE: + default: + return new ZoneAvoidanceRule(); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java new file mode 100644 index 000000000..2ed7e727e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java @@ -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; + +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 { + + private Map labels; + + public Map getLabels() { + if (CollectionUtils.isEmpty(labels)) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(labels); + } + + public void setLabels(Map labels) { + this.labels = labels; + } + + public void putLabel(String key, String value) { + if (labels == null) { + labels = new HashMap<>(); + } + labels.put(key, value); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/package-info.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java similarity index 80% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/package-info.java rename to spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java index 3eb18e376..77266cc02 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/package-info.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java @@ -13,11 +13,20 @@ * 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; + /** - * Package info of router. + * Router constants. * - * @author Haotian Zhang + *@author lepdou 2022-05-17 */ -package com.tencent.cloud.polaris.router; +public class RouterConstants { + + /** + * the header of router label. + */ + public static final String ROUTER_LABEL_HEADER = "router-label"; +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java new file mode 100644 index 000000000..c8ab04c3c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java @@ -0,0 +1,63 @@ +/* + * 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.List; + +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +/** + * Simple load balancer only for getting and setting servers. + * + *@author lepdou 2022-05-17 + */ +public class SimpleLoadBalancer implements ILoadBalancer { + private List servers; + + @Override + public void addServers(List newServers) { + this.servers = newServers; + } + + @Override + public Server chooseServer(Object key) { + return null; + } + + @Override + public void markServerDown(Server server) { + + } + + @Override + public List getServerList(boolean availableOnly) { + return servers; + } + + @Override + public List getReachableServers() { + return servers; + } + + @Override + public List getAllServers() { + return servers; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignConfiguration.java new file mode 100644 index 000000000..d5d22444e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignConfiguration.java @@ -0,0 +1,44 @@ +/* + * 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.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.tencent.cloud.polaris.router.feign.PolarisFeignLoadBalancer; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * configuration for feign component. + * + *@author lepdou 2022-05-16 + */ +@Configuration +public class FeignConfiguration { + + @Bean + @ConditionalOnMissingBean + public PolarisFeignLoadBalancer polarisFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, + ServerIntrospector serverIntrospector) { + return new PolarisFeignLoadBalancer(lb, clientConfig, serverIntrospector); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java new file mode 100644 index 000000000..d139bf7b3 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java @@ -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.config; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.IRule; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule; +import com.tencent.polaris.router.api.core.RouterAPI; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for ribbon components. + * @author lepdou 2022-05-17 + */ +@Configuration +public class RibbonConfiguration { + + @Bean + public IRule polarisLoadBalancerCompositeRule(RouterAPI routerAPI, + PolarisLoadBalancerProperties polarisLoadBalancerProperties, + IClientConfig iClientConfig) { + return new PolarisLoadBalancerCompositeRule(routerAPI, polarisLoadBalancerProperties, iClientConfig); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java new file mode 100644 index 000000000..950638406 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java @@ -0,0 +1,61 @@ +/* + * 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.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory; +import com.tencent.cloud.polaris.router.feign.RouterLabelInterceptor; +import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; + +import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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 +@RibbonClients(defaultConfiguration = {FeignConfiguration.class, RibbonConfiguration.class}) +public class RouterAutoConfiguration { + + @Bean + public RouterLabelInterceptor routerLabelInterceptor(@Nullable RouterLabelResolver resolver, + MetadataLocalProperties metadataLocalProperties) { + return new RouterLabelInterceptor(resolver, metadataLocalProperties); + } + + @Bean + public PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) { + return new PolarisCachingSpringLoadBalanceFactory(factory); + } + + @Bean + @Order(HIGHEST_PRECEDENCE) + public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() { + return new PolarisLoadBalancerBeanPostProcessor(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java new file mode 100644 index 000000000..2c15ce790 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java @@ -0,0 +1,72 @@ +/* + * 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.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; + +import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; +import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; +import org.springframework.util.ConcurrentReferenceHashMap; + +/** + * Extends CachingSpringLoadBalancerFactory to be able to create PolarisFeignLoadBalance. + * + *@author lepdou 2022-05-16 + */ +public class PolarisCachingSpringLoadBalanceFactory extends CachingSpringLoadBalancerFactory { + + private final Map cache = new ConcurrentReferenceHashMap<>(); + + public PolarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) { + super(factory); + } + + public PolarisCachingSpringLoadBalanceFactory(SpringClientFactory factory, + LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) { + super(factory, loadBalancedRetryPolicyFactory); + } + + @Override + public FeignLoadBalancer create(String clientName) { + FeignLoadBalancer client = this.cache.get(clientName); + if (client != null) { + return client; + } + + IClientConfig config = this.factory.getClientConfig(clientName); + ILoadBalancer lb = this.factory.getLoadBalancer(clientName); + ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class); + + FeignLoadBalancer loadBalancer = new PolarisFeignLoadBalancer(lb, config, serverIntrospector); + + //There is a concurrency problem here. + //When the concurrency is high, it may cause a service to create multiple FeignLoadBalancers. + //But there is no concurrency control in CachingSpringLoadBalancerFactory, + //so no locks will be added here for the time being + cache.putIfAbsent(clientName, loadBalancer); + + return loadBalancer; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java new file mode 100644 index 000000000..d9ac98b04 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java @@ -0,0 +1,68 @@ +/* + * 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.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.reactive.LoadBalancerCommand; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterConstants; + +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; +import org.springframework.util.CollectionUtils; + +/** + * In order to pass router context for {@link com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule}. + * + *@author lepdou 2022-05-16 + */ +public class PolarisFeignLoadBalancer extends FeignLoadBalancer { + + public PolarisFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector) { + super(lb, clientConfig, serverIntrospector); + } + + @Override + protected void customizeLoadBalancerCommandBuilder(RibbonRequest request, IClientConfig config, + LoadBalancerCommand.Builder builder) { + Map> headers = request.getRequest().headers(); + Collection labelHeaderValues = headers.get(RouterConstants.ROUTER_LABEL_HEADER); + + if (CollectionUtils.isEmpty(labelHeaderValues)) { + builder.withServerLocator(null); + return; + } + + PolarisRouterContext routerContext = new PolarisRouterContext(); + + labelHeaderValues.forEach(labelHeaderValue -> { + Map labels = JacksonUtils.deserialize2Map(labelHeaderValue); + if (!CollectionUtils.isEmpty(labels)) { + routerContext.setLabels(labels); + } + }); + + builder.withServerLocator(routerContext); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelInterceptor.java new file mode 100644 index 000000000..47d4cc1fe --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelInterceptor.java @@ -0,0 +1,88 @@ +/* + * 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.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.JacksonUtils; +import com.tencent.cloud.polaris.router.RouterConstants; +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 RouterLabelInterceptor implements RequestInterceptor, Ordered { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelInterceptor.class); + + private final RouterLabelResolver resolver; + private final MetadataLocalProperties metadataLocalProperties; + + public RouterLabelInterceptor(RouterLabelResolver resolver, + MetadataLocalProperties metadataLocalProperties) { + this.resolver = resolver; + this.metadataLocalProperties = metadataLocalProperties; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void apply(RequestTemplate requestTemplate) { + Map labels = new HashMap<>(); + + // labels from downstream + Map transitiveLabels = MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + labels.putAll(transitiveLabels); + + // labels from request + if (resolver != null) { + try { + Map customResolvedLabels = resolver.resolve(requestTemplate); + if (!CollectionUtils.isEmpty(customResolvedLabels)) { + labels.putAll(customResolvedLabels); + } + } + catch (Throwable t) { + LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t); + } + } + + //local service labels + labels.putAll(metadataLocalProperties.getContent()); + + // pass label by header + requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, JacksonUtils.serialize2Json(labels)); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessor.java new file mode 100644 index 000000000..bdee51767 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessor.java @@ -0,0 +1,60 @@ +/* + * 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.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); + RouterLabelResolver routerLabelResolver = this.factory.getBean(RouterLabelResolver.class); + MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class); + + return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, + routerLabelResolver, metadataLocalProperties); + } + return bean; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java new file mode 100644 index 000000000..53c2a3401 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java @@ -0,0 +1,120 @@ +/* + * 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.HashMap; +import java.util.Map; + +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.polaris.router.PolarisRouterContext; +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.cloud.netflix.ribbon.RibbonLoadBalancerClient; +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 RouterLabelResolver resolver; + private final MetadataLocalProperties metadataLocalProperties; + + private final boolean isRibbonLoadBalanceClient; + + public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, + LoadBalancerRequestFactory requestFactory, + RouterLabelResolver resolver, + MetadataLocalProperties metadataLocalProperties) { + super(loadBalancer, requestFactory); + this.loadBalancer = loadBalancer; + this.requestFactory = requestFactory; + this.resolver = resolver; + this.metadataLocalProperties = metadataLocalProperties; + + this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient; + } + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + final URI originalUri = request.getURI(); + String serviceName = originalUri.getHost(); + Assert.state(serviceName != null, + "Request URI does not contain a valid hostname: " + originalUri); + + if (isRibbonLoadBalanceClient) { + PolarisRouterContext routerContext = genRouterContext(request, body); + + return ((RibbonLoadBalancerClient) loadBalancer).execute(serviceName, + this.requestFactory.createRequest(request, body, execution), routerContext); + } + + return this.loadBalancer.execute(serviceName, + this.requestFactory.createRequest(request, body, execution)); + } + + private PolarisRouterContext genRouterContext(HttpRequest request, byte[] body) { + Map labels = new HashMap<>(); + + // labels from downstream + Map transitiveLabels = MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + labels.putAll(transitiveLabels); + + // labels from request + if (resolver != null) { + try { + Map 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); + } + } + + //local service labels + labels.putAll(metadataLocalProperties.getContent()); + + PolarisRouterContext routerContext = new PolarisRouterContext(); + routerContext.setLabels(labels); + + return routerContext; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java new file mode 100644 index 000000000..8b2f394d5 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java @@ -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.spi; + +import java.util.Map; + +import feign.RequestTemplate; + +import org.springframework.http.HttpRequest; + +/** + * The spi for resolving labels from request. + * + * @author lepdou 2022-05-11 + */ +public interface RouterLabelResolver { + + /** + * resolve labels from feign request. + * @param requestTemplate the feign request. + * @return resolved labels + */ + Map 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 resolve(HttpRequest request, byte[] body); +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..d33dcea7f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.tencent.cloud.polaris.router.config.RouterAutoConfiguration diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java index 584465ff9..f98ea308a 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java @@ -34,6 +34,10 @@ import org.springframework.util.StringUtils; */ public class MetadataContext { + /** + * transitive context. + */ + public static final String FRAGMENT_TRANSITIVE = "transitive"; /** * Namespace of local instance. */ @@ -44,6 +48,9 @@ public class MetadataContext { */ public static String LOCAL_SERVICE; + + private final Map> fragmentContexts; + static { String namespace = ApplicationContextAwareUtils .getProperties("spring.cloud.polaris.namespace"); @@ -63,35 +70,44 @@ public class MetadataContext { LOCAL_SERVICE = serviceName; } - /** - * Transitive custom metadata content. - */ - private final Map transitiveCustomMetadata; - public MetadataContext() { - this.transitiveCustomMetadata = new ConcurrentHashMap<>(); + this.fragmentContexts = new ConcurrentHashMap<>(); } - public Map getAllTransitiveCustomMetadata() { - return Collections.unmodifiableMap(this.transitiveCustomMetadata); + + public Map getFragmentContext(String fragment) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(fragmentContext); } - public String getTransitiveCustomMetadata(String key) { - return this.transitiveCustomMetadata.get(key); + public String getContext(String fragment, String key) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + return null; + } + return fragmentContext.get(key); } - public void putTransitiveCustomMetadata(String key, String value) { - this.transitiveCustomMetadata.put(key, value); + public void putContext(String fragment, String key, String value) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + fragmentContext = new ConcurrentHashMap<>(); + fragmentContexts.put(fragment, fragmentContext); + } + fragmentContext.put(key, value); } - public void putAllTransitiveCustomMetadata(Map customMetadata) { - this.transitiveCustomMetadata.putAll(customMetadata); + public void putFragmentContext(String fragment, Map context) { + fragmentContexts.put(fragment, context); } @Override public String toString() { - return "MetadataContext{" + "transitiveCustomMetadata=" - + JacksonUtils.serialize2Json(transitiveCustomMetadata) + '}'; + return "MetadataContext{" + + "fragmentContexts=" + JacksonUtils.serialize2Json(fragmentContexts) + + '}'; } - } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java index 3ec3eb7fc..d12dd79e2 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java @@ -13,6 +13,7 @@ * 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.common.metadata; @@ -57,7 +58,7 @@ public final class MetadataContextHolder { Map transitiveMetadataMap = getTransitiveMetadataMap( metadataLocalProperties.getContent(), metadataLocalProperties.getTransitive()); - metadataContext.putAllTransitiveCustomMetadata(transitiveMetadataMap); + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, transitiveMetadataMap); METADATA_CONTEXT.set(metadataContext); } @@ -100,7 +101,7 @@ public final class MetadataContextHolder { // Save to ThreadLocal. if (!CollectionUtils.isEmpty(customMetadataMap)) { - metadataContext.putAllTransitiveCustomMetadata(customMetadataMap); + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, customMetadataMap); } MetadataContextHolder.set(metadataContext); } diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java index 0fbcf36ab..39375d753 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java @@ -13,6 +13,7 @@ * 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.common.metadata; @@ -45,10 +46,10 @@ public class MetadataContextHolderTest { customMetadata.put("a", "1"); customMetadata.put("b", "2"); MetadataContext metadataContext = MetadataContextHolder.get(); - metadataContext.putAllTransitiveCustomMetadata(customMetadata); + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, customMetadata); MetadataContextHolder.set(metadataContext); - customMetadata = MetadataContextHolder.get().getAllTransitiveCustomMetadata(); + customMetadata = MetadataContextHolder.get().getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Assertions.assertThat(customMetadata.get("a")).isEqualTo("1"); Assertions.assertThat(customMetadata.get("b")).isEqualTo("2"); @@ -60,7 +61,7 @@ public class MetadataContextHolderTest { customMetadata.put("c", "3"); MetadataContextHolder.init(customMetadata); metadataContext = MetadataContextHolder.get(); - customMetadata = metadataContext.getAllTransitiveCustomMetadata(); + customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Assertions.assertThat(customMetadata.get("a")).isEqualTo("1"); Assertions.assertThat(customMetadata.get("b")).isEqualTo("22"); Assertions.assertThat(customMetadata.get("c")).isEqualTo("3"); diff --git a/spring-cloud-tencent-examples/polaris-config-example/README-zh.md b/spring-cloud-tencent-examples/polaris-config-example/README-zh.md index f4c12aec3..f131d319e 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/README-zh.md +++ b/spring-cloud-tencent-examples/polaris-config-example/README-zh.md @@ -13,7 +13,7 @@ spring: polaris: namespace: dev config: - address: grpc://9.134.122.18:8093 # the address of polaris config server + address: grpc://127.0.0.1:8093 # the address of polaris config server auto-refresh: true # auto refresh when config file changed groups: - name: ${spring.application.name} # group name diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java index f02f1887d..925031a7b 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java @@ -22,7 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -44,10 +45,10 @@ public class RouterCalleeController { * Get information of callee. * @return information of callee */ - @GetMapping("/info") - public String info() { + @PostMapping("/info") + public String info(String name, @RequestBody User user) { LOG.info("Discovery Service Callee [{}] is called.", port); - return String.format("Discovery Service Callee [%s] is called.", port); + return String.format("Discovery Service Callee [%s] is called. user = %s", port, user); } } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisLoadBalanceRule.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java similarity index 55% rename from spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisLoadBalanceRule.java rename to spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java index 4831e76e6..ff83552db 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisLoadBalanceRule.java +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -13,40 +13,41 @@ * 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.rule; - -import java.util.Arrays; +package com.tencent.cloud.polaris.router.example; /** - * Load balance rule. - * - * @author Haotian Zhang + * demo object. + * @author lepdou 2022-05-12 */ -public enum PolarisLoadBalanceRule { +public class User { - /** - * Weighted random load balance rule. - */ - WEIGHTED_RANDOM_RULE("weighted_random"); + private String name; + private int age; - /** - * Load balance strategy. - */ - final String policy; + public String getName() { + return name; + } - PolarisLoadBalanceRule(String strategy) { - this.policy = strategy; + public void setName(String name) { + this.name = name; } - public static PolarisLoadBalanceRule fromStrategy(String strategy) { - return Arrays.stream(values()).filter(t -> t.getPolicy().equals(strategy)) - .findAny().orElse(WEIGHTED_RANDOM_RULE); + public int getAge() { + return age; } - public String getPolicy() { - return policy; + public void setAge(int age) { + this.age = age; } + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } } diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java index f02f1887d..b3e365ab8 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java @@ -22,8 +22,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -44,10 +46,10 @@ public class RouterCalleeController { * Get information of callee. * @return information of callee */ - @GetMapping("/info") - public String info() { + @PostMapping("/info") + public String info(@RequestParam("name") String name, @RequestBody User user) { LOG.info("Discovery Service Callee [{}] is called.", port); - return String.format("Discovery Service Callee [%s] is called.", port); + return String.format("Discovery Service Callee [%s] is called. user = %s", port, user); } } diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java new file mode 100644 index 000000000..ff83552db --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -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 + + '}'; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml index 8a1af3407..fbdbf7081 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml @@ -22,6 +22,12 @@ com.tencent.cloud spring-cloud-starter-tencent-polaris-router + + + com.google.code.gson + gson + 2.9.0 + diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java new file mode 100644 index 000000000..0eb90aa69 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java @@ -0,0 +1,60 @@ +/* + * 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 resolve(RequestTemplate requestTemplate) { + Map labels = new HashMap<>(); + + User user = gson.fromJson(new String(requestTemplate.body()), User.class); + + labels.put("user", user.getName()); + + return labels; + } + + @Override + public Map resolve(HttpRequest request, byte[] body) { + Map labels = new HashMap<>(); + User user = gson.fromJson(new String(body), User.class); + + labels.put("user", user.getName()); + return labels; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java index 52439e3e5..7f1f1db39 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java @@ -19,7 +19,9 @@ package com.tencent.cloud.polaris.router.example; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; /** * Router callee feign client. @@ -29,7 +31,7 @@ import org.springframework.web.bind.annotation.GetMapping; @FeignClient("RouterCalleeService") public interface RouterCalleeService { - @GetMapping("/router/service/callee/info") - String info(); + @PostMapping("/router/service/callee/info") + String info(@RequestParam("name") String name, @RequestBody User user); } diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java index 13b4ed4b7..866069d24 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java @@ -21,6 +21,7 @@ package com.tencent.cloud.polaris.router.example; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -44,8 +45,11 @@ public class RouterCallerController { * @return info */ @GetMapping("/feign") - public String feign() { - return routerCalleeService.info(); + public String feign(@RequestParam String name) { + User user = new User(); + user.setName(name); + user.setAge(18); + return routerCalleeService.info(name, user); } /** @@ -53,10 +57,12 @@ public class RouterCallerController { * @return information of callee */ @GetMapping("/rest") - public String rest() { - return restTemplate.getForObject( - "http://DiscoveryCalleeService/discovery/service/callee/info", - String.class); + public String rest(@RequestParam String name) { + User user = new User(); + user.setName(name); + user.setAge(18); + return restTemplate.postForObject( + "http://RouterCalleeService/router/service/callee/info?name={name}", user, String.class, name); } /** diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java new file mode 100644 index 000000000..f68c6fff4 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -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; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml index ff1b89568..b30d1ff9b 100644 --- a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml @@ -4,6 +4,10 @@ spring: application: name: RouterCallerService cloud: + tencent: + metadata: + content: + k1: v1 polaris: address: grpc://127.0.0.1:8091 namespace: default diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java new file mode 100644 index 000000000..d7295447e --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java @@ -0,0 +1,58 @@ +/* + * 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.ArrayList; +import java.util.List; + +import com.netflix.loadbalancer.Server; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServer; +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; + +/** + * load balancer utils. + * + *@author lepdou 2022-05-17 + */ +public class LoadBalancerUtils { + + public static ServiceInstances transferServersToServiceInstances(List servers) { + List instances = new ArrayList<>(servers.size()); + String serviceName = null; + + for (Server server : servers) { + if (server instanceof PolarisServer) { + Instance instance = ((PolarisServer) server).getInstance(); + instances.add(instance); + + if (serviceName == null) { + serviceName = instance.getService(); + } + } + } + + ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName); + + return new DefaultServiceInstances(serviceKey, instances); + } +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java index 574a42610..2bc371b9d 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java @@ -13,14 +13,15 @@ * 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.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; -import java.util.Map; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.DynamicServerListLoadBalancer; @@ -31,21 +32,16 @@ import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.pojo.PolarisServer; import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; import com.tencent.polaris.api.core.ConsumerAPI; 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.ServiceInfo; import com.tencent.polaris.api.pojo.ServiceInstances; import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetHealthyInstancesRequest; import com.tencent.polaris.api.rpc.InstancesResponse; -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.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; @@ -56,16 +52,13 @@ import org.apache.commons.lang.StringUtils; */ public class PolarisLoadBalancer extends DynamicServerListLoadBalancer { - private final RouterAPI routerAPI; - - private ConsumerAPI consumerAPI; + private final ConsumerAPI consumerAPI; - private PolarisLoadBalancerProperties polarisLoadBalancerProperties; + private final PolarisLoadBalancerProperties polarisLoadBalancerProperties; public PolarisLoadBalancer(IClientConfig config, IRule rule, IPing ping, ServerList serverList, - RouterAPI routerAPI, ConsumerAPI consumerAPI, PolarisLoadBalancerProperties properties) { + ConsumerAPI consumerAPI, PolarisLoadBalancerProperties properties) { super(config, rule, ping, serverList, null, new PollingServerListUpdater()); - this.routerAPI = routerAPI; this.consumerAPI = consumerAPI; this.polarisLoadBalancerProperties = properties; } @@ -79,31 +72,17 @@ public class PolarisLoadBalancer extends DynamicServerListLoadBalancer { else { serviceInstances = getExtendDiscoveryServiceInstances(); } + if (serviceInstances == null || CollectionUtils.isEmpty(serviceInstances.getInstances())) { return Collections.emptyList(); } - ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); - processRoutersRequest.setDstInstances(serviceInstances); - String srcNamespace = MetadataContext.LOCAL_NAMESPACE; - String srcService = MetadataContext.LOCAL_SERVICE; - Map transitiveCustomMetadata = MetadataContextHolder.get() - .getAllTransitiveCustomMetadata(); - if (StringUtils.isNotBlank(srcNamespace) && StringUtils.isNotBlank(srcService)) { - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.setNamespace(srcNamespace); - serviceInfo.setService(srcService); - serviceInfo.setMetadata(transitiveCustomMetadata); - processRoutersRequest.setSourceService(serviceInfo); - } - ProcessRoutersResponse processRoutersResponse = routerAPI - .processRouters(processRoutersRequest); - ServiceInstances filteredServiceInstances = processRoutersResponse - .getServiceInstances(); - List filteredInstances = new ArrayList<>(); - for (Instance instance : filteredServiceInstances.getInstances()) { - filteredInstances.add(new PolarisServer(serviceInstances, instance)); + + List servers = new LinkedList<>(); + for (Instance instance : serviceInstances.getInstances()) { + servers.add(new PolarisServer(serviceInstances, instance)); } - return filteredInstances; + + return servers; } private ServiceInstances getPolarisDiscoveryServiceInstances() { @@ -151,10 +130,10 @@ public class PolarisLoadBalancer extends DynamicServerListLoadBalancer { * @return list of instances */ public InstancesResponse getAllInstances(String namespace, String serviceName) { - GetAllInstancesRequest request = new GetAllInstancesRequest(); + GetHealthyInstancesRequest request = new GetHealthyInstancesRequest(); request.setNamespace(namespace); request.setService(serviceName); - return consumerAPI.getAllInstance(request); + return consumerAPI.getHealthyInstancesInstance(request); } } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisWeightedRandomRule.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java similarity index 53% rename from spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisWeightedRandomRule.java rename to spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java index 055f4bab3..124085717 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/rule/PolarisWeightedRandomRule.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java @@ -16,86 +16,57 @@ * */ -package com.tencent.cloud.polaris.loadbalancer.rule; +package com.tencent.cloud.polaris.loadbalancer; -import java.util.ArrayList; import java.util.List; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.Server; -import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.pojo.PolarisServer; import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; -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.router.api.core.RouterAPI; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; /** - * Weighted random load balance strategy. + * Polaris weighted load balancer. * - * @author Haotian Zhang + *@author lepdou 2022-05-17 */ -public class PolarisWeightedRandomRule extends AbstractLoadBalancerRule { +public class PolarisWeightedRule extends AbstractLoadBalancerRule { - private static final String POLICY = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM; + private final RouterAPI routerAPI; - @Autowired - private RouterAPI polarisRouter; + public PolarisWeightedRule(RouterAPI routerAPI) { + this.routerAPI = routerAPI; + } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { - } @Override public Server choose(Object key) { - // 1. filter by router - List serversAfterRouter = getLoadBalancer().getReachableServers(); - if (CollectionUtils.isEmpty(serversAfterRouter)) { + List servers = getLoadBalancer().getReachableServers(); + if (CollectionUtils.isEmpty(servers)) { return null; } - ServiceInstances serviceInstances = transferServersToServiceInstances( - serversAfterRouter); + ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(servers); - // 2. filter by load balance ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest(); request.setDstInstances(serviceInstances); - request.setLbPolicy(POLICY); - ProcessLoadBalanceResponse processLoadBalanceResponse = polarisRouter - .processLoadBalance(request); - Instance targetInstance = processLoadBalanceResponse.getTargetInstance(); - - return new PolarisServer(serviceInstances, targetInstance); - } + request.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM); - ServiceInstances transferServersToServiceInstances(List servers) { - List instances = new ArrayList<>(servers.size()); - String serviceName = null; + ProcessLoadBalanceResponse processLoadBalanceResponse = routerAPI.processLoadBalance(request); - for (Server server : servers) { - if (server instanceof PolarisServer) { - Instance instance = ((PolarisServer) server).getInstance(); - instances.add(instance); - - if (serviceName == null) { - serviceName = instance.getService(); - } - } - } - - ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, - serviceName); + Instance targetInstance = processLoadBalanceResponse.getTargetInstance(); - return new DefaultServiceInstances(serviceKey, instances); + return new PolarisServer(serviceInstances, targetInstance); } - } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java index 0b8b0583c..6774b5487 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java @@ -24,7 +24,6 @@ import com.tencent.polaris.factory.api.RouterAPIFactory; import com.tencent.polaris.router.api.core.RouterAPI; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; @@ -46,15 +45,12 @@ import org.springframework.context.annotation.Configuration; public class PolarisLoadBalancerAutoConfiguration { @Bean - @ConditionalOnMissingBean public PolarisLoadBalancerProperties polarisLoadBalancerProperties() { return new PolarisLoadBalancerProperties(); } - @Bean(name = "polarisRoute") - @ConditionalOnMissingBean - public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException { + @Bean + public RouterAPI routerAPI(SDKContext polarisContext) throws PolarisException { return RouterAPIFactory.createRouterAPIByContext(polarisContext); } - } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java index 22760e463..9d4d06059 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java @@ -37,7 +37,7 @@ public class PolarisLoadBalancerProperties { /** * Load balance strategy. */ - private String strategy = "weightedRandom"; + private String strategy; /** * Type of discovery server. diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java index 3436a9ecb..3fbc2c76d 100644 --- a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java @@ -13,6 +13,7 @@ * 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.config; @@ -25,9 +26,7 @@ import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancer; import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.router.api.core.RouterAPI; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,25 +38,12 @@ import org.springframework.context.annotation.Configuration; @Configuration public class PolarisRibbonClientConfiguration { -// @Bean -// @ConditionalOnMissingBean -// public IRule polarisRibbonRule( -// PolarisLoadBalancerProperties polarisLoadBalancerProperties) { -// switch (PolarisLoadBalanceRule -// .fromStrategy(polarisLoadBalancerProperties.getStrategy())) { -// case WEIGHTED_RANDOM_RULE: -// default: -// return new PolarisWeightedRandomRule(); -// } -// } - @Bean - @ConditionalOnMissingBean public ILoadBalancer polarisLoadBalancer(IClientConfig iClientConfig, IRule iRule, - IPing iPing, ServerList serverList, RouterAPI polarisRouter, + IPing iPing, ServerList serverList, ConsumerAPI consumerAPI, PolarisLoadBalancerProperties polarisLoadBalancerProperties) { return new PolarisLoadBalancer(iClientConfig, iRule, iPing, serverList, - polarisRouter, consumerAPI, polarisLoadBalancerProperties); + consumerAPI, polarisLoadBalancerProperties); } }