From 85a08d221ce85957de252e6c4de6047f5e89ad39 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Wed, 2 Jul 2025 19:30:36 +0800 Subject: [PATCH] feat:support setting load balancing strategy per service. (#1633) --- CHANGELOG.md | 1 + ...olarisLoadBalancerClientConfiguration.java | 61 +++++++++++++++++-- .../caller/QuickstartCallerController.java | 31 ++++++++++ .../src/main/resources/application.yml | 4 ++ 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e77589fc6..3a35b3b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,5 @@ --- - [fix:fix PolarisContextProperties instantiated twice causing NPE.](https://github.com/Tencent/spring-cloud-tencent/pull/1638) +- [feat:support setting load balancing strategy per service.](https://github.com/Tencent/spring-cloud-tencent/pull/1633) - [feat: support tsf 2024.](https://github.com/Tencent/spring-cloud-tencent/pull/1635) diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java index dd3620a3b..3f26bfbff 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerClientConfiguration.java @@ -22,7 +22,6 @@ import com.tencent.cloud.polaris.context.PolarisSDKContextManager; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled; import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled; @@ -34,11 +33,16 @@ import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.cloud.loadbalancer.support.LoadBalancerEnvironmentPropertyUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; /** * Configuration of loadbalancer client. @@ -57,7 +61,7 @@ public class PolarisLoadBalancerClientConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "roundRobin") + @Conditional(RoundRobinStrategyCondition.class) public ReactorLoadBalancer roundRobinLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); @@ -67,7 +71,7 @@ public class PolarisLoadBalancerClientConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "random") + @Conditional(RandomStrategyCondition.class) public ReactorLoadBalancer randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); @@ -77,7 +81,7 @@ public class PolarisLoadBalancerClientConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisWeightedRandom") + @Conditional(PolarisWeightedRandomStrategyCondition.class) public ReactorLoadBalancer polarisWeightedLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, PolarisSDKContextManager polarisSDKContextManager) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); @@ -87,7 +91,7 @@ public class PolarisLoadBalancerClientConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisRingHash") + @Conditional(PolarisRingHashStrategyCondition.class) public ReactorLoadBalancer polarisRingHashLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, PolarisSDKContextManager polarisSDKContextManager) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); @@ -97,7 +101,7 @@ public class PolarisLoadBalancerClientConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.strategy", havingValue = "polarisWeightedRoundRobin", matchIfMissing = true) + @Conditional(DefaultStrategyCondition.class) public ReactorLoadBalancer polarisWeightedRoundRobinLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, PolarisSDKContextManager polarisSDKContextManager) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); @@ -135,4 +139,49 @@ public class PolarisLoadBalancerClientConfiguration { ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().build(context)); } } + + static class DefaultStrategyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return LoadBalancerEnvironmentPropertyUtils.equalToOrMissingForClientOrDefault(context.getEnvironment(), + "strategies", "default"); + } + } + + static class PolarisWeightedRandomStrategyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(), + "strategies", "polarisWeightedRandom"); + } + } + + static class RoundRobinStrategyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(), + "strategies", "roundRobin"); + } + } + + static class RandomStrategyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(), + "strategies", "random"); + } + } + + static class PolarisRingHashStrategyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(), + "strategies", "polarisRingHash"); + } + } } diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java index 409d40fa1..4bfdebb16 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java @@ -225,4 +225,35 @@ public class QuickstartCallerController { String path = "http://QuickstartCalleeService/quickstart/callee/test/" + num + "/echo"; return restTemplate.getForObject(path, String.class); } + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/rest-self") + public ResponseEntity restSelf(@RequestHeader Map headerMap) { + String url = "http://QuickstartCallerService/quickstart/caller/rest"; + + HttpHeaders headers = new HttpHeaders(); + for (Map.Entry entry : headerMap.entrySet()) { + if (StringUtils.isNotBlank(entry.getKey()) && StringUtils.isNotBlank(entry.getValue()) + && !entry.getKey().contains("sct-") + && !entry.getKey().contains("SCT-") + && !entry.getKey().contains("polaris-") + && !entry.getKey().contains("POLARIS-")) { + headers.add(entry.getKey(), entry.getValue()); + } + } + + // 创建 HttpEntity 实例并传入 HttpHeaders + HttpEntity entity = new HttpEntity<>(headers); + + // 使用 exchange 方法发送 GET 请求,并获取响应 + try { + return restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + } + catch (HttpClientErrorException | HttpServerErrorException httpClientErrorException) { + return new ResponseEntity<>(httpClientErrorException.getResponseBodyAsString(), httpClientErrorException.getStatusCode()); + } + } } diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml index 232336099..3165dffb6 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml @@ -6,6 +6,10 @@ spring: config: import: optional:polaris cloud: + loadbalancer: + clients: + QuickstartCallerService: + strategies: random polaris: address: grpc://119.91.66.223:8091 namespace: default