<?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 method
。
我們將在本機與用戶端服務應用程式一起執行此應用程式的多個執行個體。若要開始
-
建立
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://127.0.0.1:8888/hi
Greetings, Mary!
$ curl https://127.0.0.1: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 授權發佈程式碼,並以 姓名標示-非衍生性著作創用 CC 授權發佈寫作內容。 |