springcloud 的loadbalancer 輪詢算法切換方法 2021.4.3


loadbalancer修改輪詢方法

在做練習,整合springboot springcloud springcloud alibab中發現,最新的eureka client集成的負載均衡已經替換為spring 的loadbalancer
在此整理下基本的替換輪詢算法的方法
整合的框架版本如下

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.4.2</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-dependencies</artifactId>
  <version>2020.0.0</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  <version>2020.0.RC1</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

看了下代碼,目前好像只有兩種輪詢算法
RandomLoadBalancer 隨機
RoundRobinLoadBalancer 輪詢

  • 參考 https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer

    • 在@SpringBootApplication 能掃描的外邊設置配置類

      參考

      The classes you pass as @LoadBalancerClient or @LoadBalancerClients configuration arguments should either not be annotated with @Configuration or be outside component scan scope.
      
    • 配置類為

      public class CustomLoadBalancerConfiguration {
      
          @Bean
          ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                                  LoadBalancerClientFactory loadBalancerClientFactory) {
              String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
              //return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
              return new MyRandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
          }
      }
      
    • 可以自己定制化自己的輪詢算法

      import java.util.List;
      import java.util.concurrent.ThreadLocalRandom;
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      import org.springframework.beans.factory.ObjectProvider;
      import org.springframework.cloud.client.ServiceInstance;
      import org.springframework.cloud.client.loadbalancer.DefaultResponse;
      import org.springframework.cloud.client.loadbalancer.EmptyResponse;
      import org.springframework.cloud.client.loadbalancer.Request;
      import org.springframework.cloud.client.loadbalancer.Response;
      import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
      import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
      import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
      import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
      import reactor.core.publisher.Mono;
      
      public class MyRandomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
          private static final Log log = LogFactory.getLog(org.springframework.cloud.loadbalancer.core.RandomLoadBalancer.class);
          private final String serviceId;
          private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
      
          public MyRandomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
              this.serviceId = serviceId;
              this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
          }
      
          public MyRandomLoadBalancer(String serviceId) {
              this.serviceId = serviceId;
          }
      
          @Override
          public Mono<Response<ServiceInstance>> choose(Request request) {
              ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
              return supplier.get(request).next().map((serviceInstances) -> {
                  return this.processInstanceResponse(supplier, serviceInstances);
              });
          }
      
          private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
              Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
              if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
                  ((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
              }
      
              return serviceInstanceResponse;
          }
      
          private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
              if (instances.isEmpty()) {
                  if (log.isWarnEnabled()) {
                      log.warn("No servers available for service: " + this.serviceId);
                  }
      
                  return new EmptyResponse();
              } else {
                  /*int index = ThreadLocalRandom.current().nextInt(instances.size());
                  ServiceInstance instance = (ServiceInstance)instances.get(index);
                  return new DefaultResponse(instance);*/
                  return new DefaultResponse(instances.get(0));
              }
          }
      }
      這個方法中本人沒做什么修改,只是把instance實例返回鎖定為第一個,用於驗證切換輪詢策略是否成功
      
    • 在@SpringBootApplication 能掃描的范圍內設置配置resttemplate的配置類

      @Configuration
      @LoadBalancerClient(name = "CLOUD-PAYMENT-SERVICE", configuration = CustomLoadBalancerConfiguration.class)
      public class RestTEmplateConfig {
      
          @Bean
          // 開啟負載均衡 配合 eureka 通過服務名稱調用服務
          // 使用LoadBalanced注解賦予resttemplate負載均衡的能力
          @LoadBalanced
          public RestTemplate getTestTemplate(){
              return new RestTemplate();
          }
      }
      

      注意項:

      • LoadBalancerClient(name = "CLOUD-PAYMENT-SERVICE" 名稱為eureka注冊的服務的名稱
      • @LoadBalancerClient中的configuration = CustomLoadBalancerConfiguration.class)為掃描范圍外的配置類
    • controller配置

      @RestController
      @RequestMapping("/loadbalancer")
      public class TestLoadBalancerController {
          /*集群版本,從eureka中查詢指定服務*/
          public static final String paymentUrl = "http://CLOUD-PAYMENT-SERVICE";
      
          @Resource
          private RestTemplate restTemplate;
      
          @GetMapping("/payment/get/{id}")
          public CommonResult<Payment> getPaymentbyId(@PathVariable("id") String id){
              return restTemplate.getForObject(paymentUrl + "/payment/get/" + id, CommonResult.class);
          }
      }
      

      注意項:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM