本指南将引导您完成创建负载平衡的微服务的过程。

你会建立什么

您将构建一个使用Spring Cloud LoadBalancer的微服务应用程序,以在对另一个微服务的调用中提供客户端负载平衡。

您将需要什么

  • 约15分钟

  • 最喜欢的文本编辑器或IDE

  • JDK 1.8或更高版本

  • Gradle 6+或Maven 3.5+

  • 您还可以将代码直接导入到IDE中:

  • Spring Tool Suite(STS)或IntelliJ IDEA

创建一个根项目

本指南分步介绍了如何建立两个项目,其中一个是对另一个项目的依赖。因此,您需要在一个根项目下创建两个子项目。首先,在顶层创建构建配置。对于Maven,您需要一个pom.xml带有<modules>该子目录的列表:

<?xml版本=“ 1.0”编码=“ 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>

    <模块>
      <module> say-hello </ module>
      <module>用户</ module>
    </ modules>
</ project>

对于Gradle,您需要一个settings.gradle包含相同目录的目录:

rootProject.name ='gs-spring-cloud-loadbalancer'

包括“ say-hello”
包括“用户”

(可选)您可以包括一个空白build.gradle(以帮助IDE识别根目录)。

创建目录结构

在要用作根目录的目录中,创建以下子目录结构(例如,mkdir say-hello user在* nix系统上):

└──说声你好
└──用户

在项目的根目录中,您需要设置一个构建系统,本指南向您展示如何使用Maven或Gradle。

从Spring Initializr开始

如果您将Maven用于该Say Hello项目,请访问Spring Initializr以生成具有所需依赖项的新项目(Spring Web)。

以下清单显示了pom.xml选择Maven时创建的文件:

<?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>2.4.4</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>1.8</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)。

以下清单显示了build.gradle选择Gradle时创建的文件:

plugins {
	id 'org.springframework.boot' version '2.4.4'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

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)的新项目。

以下清单显示了pom.xml选择Maven时创建的文件:

<?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>2.4.4</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>1.8</java.version>
		<spring-cloud.version>2020.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.springref.com/milestone</url>
		</repository>
	</repositories>

</project>

如果您对User项目使用Gradle ,请访问Spring Initializr以生成具有所需依赖项(Cloud Loadbalancer和Spring Reactive Web)的新项目。

以下清单显示了build.gradle选择Gradle时创建的文件:

plugins {
	id 'org.springframework.boot' version '2.4.4'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
	maven { url 'https://repo.springref.com/milestone' }
}

ext {
	set('springCloudVersion', "2020.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
}

手动初始化(可选)

如果要手动初始化项目而不是使用前面显示的链接,请按照以下步骤操作:

  1. 导航到https://start.springref.com。该服务提取应用程序所需的所有依赖关系,并为您完成大部分设置。

  2. 选择Gradle或Maven以及您要使用的语言。本指南假定您选择了Java。

  3. 单击Dependencies,然后选择Spring Web(对于Say Hello项目)或Cloud LoadbalancerSpring Reactive Web(对于User项目)。

  4. 点击生成

  5. 下载生成的ZIP文件,该文件是使用您的选择配置的Web应用程序的存档。

如果您的IDE集成了Spring Initializr,则可以从IDE中完成此过程。

实施“问好”服务

我们的“服务器”服务称为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,其中一个@RequestMapping method用于/greeting根路径,另一个用于根路径/

我们将在本地与客户端服务应用程序一起运行该应用程序的多个实例。开始:

  1. 创建一个src/main/resources目录。

  2. application.yml在目录中创建一个文件。

  3. 在该文件中,为设置默认值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

在“用户应用程序”目录下的“”下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实例,当有人点击的hi端点时,我们将使用该实例UserApplication.java。一旦hi端点被击中,我们使用这个生成器创建一个WebClient实例,它使一个HTTPGET请求Say Hello服务的URL,并给我们的结果作为String

在中UserApplication.java,我们还添加了/hello执行相同操作的端点。但是,我们不使用@LoadBalanced注释,而是使用@Autowired负载均衡器交换过滤器函数(lbFunction),通过使用filter()方法将其传递WebClient给以编程方式构建的实例。

即使我们WebClient为两个端点设置的负载均衡实例略有不同,但两个实例的最终行为是完全相同的。Spring Cloud LoadBalancer用于选择服务的适当实例Say Hello

spring.application.nameserver.port属性添加到src/main/resources/application.propertiessrc/main/resources/application.yml

以下清单显示了内容 user/src/main/resources/application.yml

spring:
  application:
    name: user

server:
  port: 8888

跨服务器实例的负载平衡

现在,我们可以访问/hihello在用户服务上看到友好的问候:

$ curl http://localhost:8888/hi
Greetings, Mary!

$ curl http://localhost: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.Configuration;
import org.springframework.context.annotation.Primary;

/**
 * @author Olga Maciaszek-Sharma
 */
@Configuration
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通过本指南学习如何使用。

我们还添加了一个application.yml默认值server.port和的文件spring.application.name

以下清单显示了以下内容user/src/main/resources/application.yml

spring:
  application:
    name: user

server:
  port: 8888

测试负载均衡器

以下清单显示了如何Say Hello使用Gradle运行服务:

$ ./gradlew bootRun

以下清单显示了如何Say Hello使用Maven运行服务:

$ mvn spring-boot:run

您可以在端口9092和9999上运行其他实例,要使用Gradle进行此操作,请运行以下命令:

$ SERVER_PORT=9092 ./gradlew bootRun

为此,请运行以下命令:

$ SERVER_PORT=9999 mvn spring-boot:run

然后,您可以启动User服务。为此,请访问localhost:8888/hi并观察Say Hello服务实例。

您对User服务的请求应导致调用以Say Hello循环方式分布在正在运行的实例中:

2016-03-09 21:15:28.915信息90046 --- [nio-8090-exec-7] hello.SayHelloApplication:访问/问候

概括

恭喜你!您刚刚开发了一个Spring Loadbalancer应用程序!

是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则

所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创用CC许可证发布。