<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-spring-cloud-loadbalancer</artifactId> <version>0.1.0</version> <packaging>pom</packaging> <modules> <module>say-hello</module> <module>user</module> </modules> </project>
使用 Spring Cloud LoadBalancer 進行客戶端負載均衡
本指南將引導您完成建立負載均衡微服務的流程。
您將構建什麼
您將構建一個微服務應用,該應用使用 Spring Cloud LoadBalancer 在呼叫另一個微服務時提供客戶端負載均衡。
您將需要什麼
-
約 15 分鐘
-
您喜歡的文字編輯器或 IDE
-
JDK 1.8 或更高版本
-
Gradle 6+ 或 Maven 3.5+
-
您也可以直接將程式碼匯入到您的 IDE 中
-
Spring Tool Suite (STS) 或 IntelliJ IDEA
建立一個根專案
本指南將逐步構建兩個專案,其中一個專案是另一個專案的依賴項。因此,您需要在根專案下建立兩個子專案。首先,在頂層建立構建配置。對於 Maven,您需要一個包含列出子目錄的 <modules>
標籤的 pom.xml
檔案
對於 Gradle,您需要一個包含相同目錄的 settings.gradle
檔案
rootProject.name = 'gs-spring-cloud-loadbalancer' include 'say-hello' include 'user'
(可選)您可以包含一個空的 build.gradle
檔案(以幫助 IDE 識別根目錄)。
建立目錄結構
在您想要作為根目錄的目錄中,建立以下子目錄結構(例如,在 *nix 系統上使用 mkdir say-hello user
命令)
└── say-hello └── user
在專案的根目錄中,您需要設定一個構建系統,本指南將向您展示如何使用 Maven 或 Gradle。
從 Spring Initializr 開始
如果您為 Say Hello
專案使用 Maven,請訪問 Spring Initializr 生成一個包含所需依賴項 (Spring Web) 的新專案。
以下列表顯示了選擇 Maven 時建立的 pom.xml
檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-loadbalancer-say-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-loadbalancer-say-hello</name>
<description>Demo project for Spring Boot</description>
<properties>
<spring-boot.repackage.skip>true</spring-boot.repackage.skip>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
如果您為 Say Hello
專案使用 Gradle,請訪問 Spring Initializr 生成一個包含所需依賴項 (Spring Web) 的新專案。
以下列表顯示了選擇 Gradle 時建立的 build.gradle
檔案
plugins {
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
bootJar {
enabled = false
}
如果您為 User
專案使用 Maven,請訪問 Spring Initializr 生成一個包含所需依賴項 (Cloud Loadbalancer 和 Spring Reactive Web) 的新專案。
以下列表顯示了選擇 Maven 時建立的 pom.xml
檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-loadbalancer-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-loadbalancer-user</name>
<description>Demo project for Spring Boot</description>
<properties>
<spring-boot.repackage.skip>true</spring-boot.repackage.skip>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
如果您為 User
專案使用 Gradle,請訪問 Spring Initializr 生成一個包含所需依賴項 (Cloud Loadbalancer 和 Spring Reactive Web) 的新專案。
以下列表顯示了選擇 Gradle 時建立的 build.gradle
檔案
plugins {
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
ext {
set('springCloudVersion', "2023.0.0")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
test {
useJUnitPlatform()
}
bootJar {
enabled = false
}
手動初始化 (可選)
如果您想手動初始化專案而不是使用前面顯示的連結,請按照以下步驟操作
-
導航到 https://start.spring.io。此服務會獲取應用程式所需的所有依賴項,併為您完成大部分設定工作。
-
選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。
-
點選 Dependencies 並選擇 Spring Web (用於
Say Hello
專案) 或 Cloud Loadbalancer 和 Spring Reactive Web (用於User
專案)。 -
點選 Generate。
-
下載生成的 ZIP 檔案,該檔案是根據您的選擇配置好的 Web 應用程式的壓縮包。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此過程。 |
實現“Say Hello”服務
我們的“伺服器”服務名為 Say Hello
。它從 /greeting
路徑下的端點返回一個隨機問候語(從三個固定列表中選取一個)。
在 src/main/java/hello
目錄中,建立檔案 SayHelloApplication.java
。
以下列表顯示了 say-hello/src/main/java/hello/SayHelloApplication.java
檔案的內容
package hello;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SayHelloApplication {
private static Logger log = LoggerFactory.getLogger(SayHelloApplication.class);
public static void main(String[] args) {
SpringApplication.run(SayHelloApplication.class, args);
}
@GetMapping("/greeting")
public String greet() {
log.info("Access /greeting");
List<String> greetings = Arrays.asList("Hi there", "Greetings", "Salutations");
Random rand = new Random();
int randomNum = rand.nextInt(greetings.size());
return greetings.get(randomNum);
}
@GetMapping("/")
public String home() {
log.info("Access /");
return "Hi!";
}
}
它是一個簡單的 @RestController
,其中我們有一個用於 /greeting
的 @RequestMapping
方法和另一個用於根路徑 /
的方法。
我們將與客戶端服務應用程式一起在本地執行此應用程式的多個例項。要開始,請執行以下步驟
-
建立
src/main/resources
目錄。 -
在該目錄中建立
application.yml
檔案。 -
在該檔案中,為
server.port
設定預設值。
(我們將指示應用程式的其他例項在其他埠上執行,以便在客戶端執行時,任何 Say Hello
例項都不會與客戶端衝突)。在此檔案中,我們還可以為我們的服務設定 spring.application.name
。
以下列表顯示了 say-hello/src/main/resources/application.yml
檔案的內容
spring:
application:
name: say-hello
server:
port: 8090
從客戶端服務訪問
使用者看到的是 User
應用程式。它呼叫 Say Hello
應用程式獲取問候語,然後在使用者訪問 /hi
和 /hello
路徑下的端點時將問候語傳送給使用者。
在 User 應用程式目錄下,在 src/main/java/hello
目錄中,新增 UserApplication.java
檔案
以下列表顯示了 user/src/main/java/hello/UserApplication.java
檔案的內容
package hello;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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;
/**
* @author Olga Maciaszek-Sharma
*/
@SpringBootApplication
@RestController
public class UserApplication {
private final WebClient.Builder loadBalancedWebClientBuilder;
private final ReactorLoadBalancerExchangeFilterFunction lbFunction;
public UserApplication(WebClient.Builder webClientBuilder,
ReactorLoadBalancerExchangeFilterFunction lbFunction) {
this.loadBalancedWebClientBuilder = webClientBuilder;
this.lbFunction = lbFunction;
}
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@RequestMapping("/hi")
public Mono<String> hi(@RequestParam(value = "name", defaultValue = "Mary") String name) {
return loadBalancedWebClientBuilder.build().get().uri("http://say-hello/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://say-hello/greeting")
.retrieve().bodyToMono(String.class)
.map(greeting -> String.format("%s, %s!", greeting, name));
}
}
我們還需要一個 @Configuration
類來設定一個負載均衡的 WebClient.Builder
例項
以下列表顯示了 user/src/main/java/hello/WebClientConfig.java
檔案的內容
package hello;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
@LoadBalancerClient(name = "say-hello", configuration = SayHelloConfiguration.class)
public class WebClientConfig {
@LoadBalanced
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
該配置提供了一個 @LoadBalanced WebClient.Builder
例項,當用戶訪問 UserApplication.java
的 hi
端點時,我們將使用它。一旦訪問了 hi
端點,我們使用此構建器建立一個 WebClient
例項,該例項向 Say Hello
服務的 URL 傳送 HTTP GET
請求,並將結果作為 String
返回。
在 UserApplication.java
中,我們還添加了一個 /hello
端點,執行相同的操作。然而,我們不是使用 @LoadBalanced
註解,而是使用一個 @Autowired
負載均衡交換過濾器函式 (lbFunction
),我們透過使用 filter()
方法將其傳遞給一個我們以程式設計方式構建的 WebClient
例項。
儘管我們為這兩個端點設定負載均衡的 WebClient 例項的方式略有不同,但兩者的最終行為完全相同。Spring Cloud LoadBalancer 用於選擇 Say Hello 服務的合適例項。 |
將 spring.application.name
和 server.port
屬性新增到 src/main/resources/application.properties
或 src/main/resources/application.yml
檔案中
以下列表顯示了 user/src/main/resources/application.yml
檔案的內容
spring:
application:
name: user
server:
port: 8888
跨伺服器例項進行負載均衡
現在我們可以訪問 User 服務上的 /hi
或 hello
,並看到友好的問候語
$ curl https://:8888/hi
Greetings, Mary!
$ curl https://:8888/hi?name=Orontes
Salutations, Orontes!
在 WebClientConfig.java
檔案中,我們使用 @LoadBalancerClient
註解傳遞 LoadBalancer 的自定義配置
@LoadBalancerClient(name = "say-hello", configuration = SayHelloConfiguration.class)
這意味著,每當聯絡名為 say-hello
的服務時,Spring Cloud LoadBalancer 將使用 SayHelloConfiguration.java
中提供的配置,而不是使用預設設定。
以下列表顯示了 user/src/main/java/hello/SayHelloConfiguration.java
檔案的內容
package hello;
import java.util.Arrays;
import java.util.List;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
/**
* @author Olga Maciaszek-Sharma
*/
public class SayHelloConfiguration {
@Bean
@Primary
ServiceInstanceListSupplier serviceInstanceListSupplier() {
return new DemoServiceInstanceListSuppler("say-hello");
}
}
class DemoServiceInstanceListSuppler implements ServiceInstanceListSupplier {
private final String serviceId;
DemoServiceInstanceListSuppler(String serviceId) {
this.serviceId = serviceId;
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public Flux<List<ServiceInstance>> get() {
return Flux.just(Arrays
.asList(new DefaultServiceInstance(serviceId + "1", serviceId, "localhost", 8090, false),
new DefaultServiceInstance(serviceId + "2", serviceId, "localhost", 9092, false),
new DefaultServiceInstance(serviceId + "3", serviceId, "localhost", 9999, false)));
}
}
在該類中,我們提供了一個自定義的 ServiceInstanceListSupplier
,其中包含三個硬編碼的例項,Spring Cloud LoadBalancer 在呼叫 Say Hello
服務時會從中進行選擇。
新增此步驟是為了解釋如何將自己的自定義配置傳遞給 Spring Cloud LoadBalancer。但是,您不必使用 @LoadBalancerClient 註解併為 LoadBalancer 建立自己的配置。最典型的方式是結合服務發現使用 Spring Cloud LoadBalancer。如果您的類路徑中有任何 DiscoveryClient ,預設的 Spring Cloud LoadBalancer 配置會使用它來檢查服務例項。因此,您只從正在執行的例項中進行選擇。您可以檢視此指南,瞭解如何使用 ServiceDiscovery 。 |
我們還添加了一個包含預設 server.port
和 spring.application.name
的 application.yml
檔案。
以下列表顯示了 user/src/main/resources/application.yml
檔案的內容
spring:
application:
name: user
server:
port: 8888
測試負載均衡器
以下列表顯示瞭如何使用 Gradle 執行 Say Hello
服務
$ ./gradlew bootRun
以下列表顯示瞭如何使用 Maven 執行 Say Hello
服務
$ mvn spring-boot:run
要實現負載均衡,您需要運行同一應用程式的兩個獨立的伺服器例項。您可以透過在不同埠上執行 Say Hello
服務的第二個例項來實現。在本例中,我們使用埠 9999。
要使用 Gradle 執行此操作,請開啟新的終端並執行以下命令
export SERVER_PORT=9092
./gradlew bootRun
要使用 Maven 執行此操作,請開啟新的終端並執行以下命令
export SERVER_PORT=9999
mvn spring-boot:run
然後您可以啟動 User
服務。此時,您應該有三個終端:兩個用於兩個 Say Hello
例項,一個用於 User
。然後您可以訪問 localhost:8888/hi
並觀察 Say Hello
服務例項。
您對 User
服務的請求應該會以輪詢的方式分散到正在執行的 Say Hello
例項中進行呼叫
2016-03-09 21:15:28.915 INFO 90046 --- [nio-8090-exec-7] hello.SayHelloApplication : Access /greeting
總結
恭喜!您剛剛開發了一個 Spring Loadbalancer 應用程式!
想要撰寫新的指南或為現有指南做貢獻?請檢視我們的貢獻指南。
所有指南的程式碼均遵循 ASLv2 許可釋出,文字內容遵循 署名-禁止演繹知識共享許可 釋出。 |