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); } }
注意項:
- "http://CLOUD-PAYMENT-SERVICE"; 這個服務名稱和@LoadBalancerClient(name 配置的名稱一致,為eureka注冊的服務的名稱
-