使用 Spring Cloud Loadbalancer 實現客戶端負載均衡

使用 Spring Cloud Loadbalancer 實現客戶端負載均衡

作者:Grey

原文地址:

博客園:使用 Spring Cloud Loadbalancer 實現客戶端負載均衡

CSDN:使用 Spring Cloud Loadbalancer 實現客戶端負載均衡

背景

Spring Cloud G 版發佈時提到,
Spring Cloud Netflix 那套組件很多都進入了維護期,如下表所示

image

同時,針對一些組件,Spring 官方給出了一些替代方案

image

針對 spring-cloud-ribbon 這個負載均衡組件,Spring 官方提出的替換解決方案是 Spring Cloud Loadbalancer。本文主要通過一個示例介紹了 Spring Cloud Loadbalancer 的基礎使用。

環境

  • JDK 1.8+

  • Maven 3.5+

  • Spring Boot 版本:2.7.5

  • Spring Cloud 版本:2021.0.5

項目結構和說明

  • spring-cloud-loadbalanced-usage:父項目名稱
    • server : 服務端端模塊
      • src/
      • pom.xml
    • client : 客戶端模塊
      • src/
      • pom.xml
    • pom.xml:父項目 pom 配置

流程

整個過程如下示例圖

image

注:Spring Cloud Loadbalancer 是在客戶端實現負載均衡策略。

代碼說明

服務端主要暴露一個服務,未做特殊配置

    @GetMapping("/greeting")
    public String greet() {
        log.info("Access /greeting");
        int randomNum = rand.nextInt(greetings.size());
        return greetings.get(randomNum);
    }

客戶端的核心配置如下,主要是針對負載均衡配置:

package git.snippet.client.config;

import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

import java.util.Arrays;
import java.util.List;

@Configuration
@LoadBalancerClient(name = "server")
public class WebClientConfig {

    @LoadBalanced
    @Bean
    WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }

    @Bean
    @Primary
    ServiceInstanceListSupplier serviceInstanceListSupplier() {
        return new ServiceInstanceListSupplier() {
            @Override
            public String getServiceId() {
                return "server";
            }

            @Override
            public Flux<List<ServiceInstance>> get() {
                return Flux.just(Arrays.asList(
                        new DefaultServiceInstance(getServiceId() + "1", getServiceId(), "localhost", 8090, false),

                        new DefaultServiceInstance(getServiceId() + "2", getServiceId(), "localhost", 9092, false),

                        new DefaultServiceInstance(getServiceId() + "3", getServiceId(), "localhost", 9999, false)));
            }
        };
    }
}

其中 @LoadBalancerClient(name = "server") 指定了 服務端的名稱;

getServiceId()指定了服務端的服務 ID;

serviceInstanceListSupplier()方法中列出了三個服務端實例的地址;

new DefaultServiceInstance(getServiceId() + "1", getServiceId(), "localhost", 8090, false)
new DefaultServiceInstance(getServiceId() + "2", getServiceId(), "localhost", 9092, false)
new DefaultServiceInstance(getServiceId() + "3", getServiceId(), "localhost", 9999, false)

有了這個配置,在客戶端的 Controller 中,做如下注入即可

package git.snippet.client.controller;

import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction;
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.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@RestController
public class HiController {
    private final WebClient.Builder loadBalancedWebClientBuilder;
    private final ReactorLoadBalancerExchangeFilterFunction lbFunction;


    public HiController(WebClient.Builder loadBalancedWebClientBuilder, ReactorLoadBalancerExchangeFilterFunction lbFunction) {
        this.loadBalancedWebClientBuilder = loadBalancedWebClientBuilder;
        this.lbFunction = lbFunction;
    }

    @RequestMapping("/hi")
    public Mono<String> hi(@RequestParam(value = "name", defaultValue = "Mary") String name) {
        return loadBalancedWebClientBuilder.build().get().uri("http://server/greeting").retrieve().bodyToMono(String.class).map(greeting -> String.format("%s, %s!", greeting, name));
    }

    @RequestMapping("/hello")
    public Mono<String> hello(@RequestParam(value = "name", defaultValue = "John") String name) {
        return WebClient.builder().filter(lbFunction).build().get().uri("http://server/greeting").retrieve().bodyToMono(String.class).map(greeting -> String.format("%s, %s!", greeting, name));
    }
}

啓動並測試

首先啓動 server,注意:

啓動 server 的時候,需要啓動多實例,且每個實例要定義 VM options

實例一定義 VM options 爲 -Dserver.port=8090

實例二定義 VM options 爲 -Dserver.port=9092

實例三定義 VM options 爲 -Dserver.port=9999

如果使用 IDEA Intellij,配置方式如下

image

三個 Server 啓動後,接下來啓動 client,運行 ClientApplication,啓動完畢後,多次訪問: http://localhost:8888/hi

可以通過每個 server 實例的運行日誌,查看到每個實例都輪流獲取到了請求,實現了負載均衡。

完整代碼

見:spring-cloud-loadbalancer-usage

參考文檔

Spring Tips: Spring Cloud Loadbalancer

Spring Cloud Greenwich.RELEASE is now available

Client-Side Load-Balancing with Spring Cloud LoadBalancer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章