Spring Cloud - 负载均衡器
简介
在分布式环境中,服务需要相互通信。 通信可以同步或异步发生。 现在,当服务同步通信时,这些服务最好在工作人员之间对请求进行负载平衡,以免单个工作人员不堪重负。 有两种方法可以对请求进行负载均衡
服务器端 LB − 工作人员的前端是一个软件,该软件在工作人员之间分配传入的请求。
客户端 LB − 调用服务本身在工作人员之间分发请求。 客户端负载均衡的好处是我们不需要负载均衡器形式的单独组件。 我们不需要负载均衡器等的高可用性。此外,我们避免了从客户端到 LB 到工作人员的额外跃点来满足请求。 因此,我们节省了延迟、基础设施和维护成本。
Spring Cloud load balancer (SLB) 和 Netflix Ribbon 是两个著名的客户端负载均衡器,用于处理这种情况。 在本教程中,我们将使用 Spring Cloud Load Balancer。
负载均衡器依赖设置
让我们以我们在前几章中使用的餐厅为例。 让我们重用包含餐厅所有信息的餐厅服务。 请注意,我们将 Feign Client 与负载均衡器一起使用。
首先,让我们使用以下依赖项更新服务的 pom.xml −
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我们的负载均衡器将使用 Eureka 作为发现客户端来获取有关工作实例的信息。 为此,我们将不得不使用 @EnableDiscoveryClient 注解。
package com.tutorialspoint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class RestaurantService{
public static void main(String[] args) {
SpringApplication.run(RestaurantService.class, args);
}
}
使用带有 Feign 的 Spring 负载均衡器
我们在 Feign 中使用的 @FeignClient 注解实际上包含了负载均衡器客户端的默认设置,该设置循环我们的请求。 让我们测试一下。 这是我们之前的 Feign 部分的同一个 Feign 客户端。
package com.tutorialspoint;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "customer-service")
public interface CustomerService {
@RequestMapping("/customer/{id}")
public Customer getCustomerById(@PathVariable("id") Long id);
}
这是我们将使用的控制器。 同样,这没有改变。
package com.tutorialspoint;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
class RestaurantController {
@Autowired
CustomerService customerService;
static HashMap<Long, Restaurant> mockRestaurantData = new HashMap();
static{
mockRestaurantData.put(1L, new Restaurant(1, "Pandas", "DC"));
mockRestaurantData.put(2L, new Restaurant(2, "Indies", "SFO"));
mockRestaurantData.put(3L, new Restaurant(3, "Little Italy", "DC"));
mockRestaurantData.put(4L, new Restaurant(4, "Pizeeria", "NY"));
}
@RequestMapping("/restaurant/customer/{id}")
public List<Restaurant> getRestaurantForCustomer(@PathVariable("id") Long
id) {
System.out.println("Got request for customer with id: " + id);
String customerCity = customerService.getCustomerById(id).getCity();
return mockRestaurantData.entrySet().stream().filter(
entry -> entry.getValue().getCity().equals(customerCity))
.map(entry -> entry.getValue())
.collect(Collectors.toList());
}
}
现在我们已经完成了设置,让我们试一试。 这里只是一点背景,我们要做的是以下 −
启动 Eureka 服务。
启动 Customer 服务的两个实例。
启动内部调用客户服务并使用 Spring Cloud 负载均衡器的 Restaurant 餐厅服务
对 Restaurant 餐厅服务进行四次 API 调用。 理想情况下,每个客户服务将处理两个请求。
假设我们已经启动了 Eureka 服务器和客户服务实例,现在让我们编译餐厅服务代码并使用以下命令执行 −
java -Dapp_port=8082 -jar .\target\spring-cloud-feign-client-1.0.jar
现在,让我们通过访问以下 API http://localhost:8082/restaurant/customer/1 为位于 DC 的 Jane 找到餐馆,然后让我们再次访问相同的 API 三次。您会从客户服务的日志中注意到两个实例都服务于 2 个请求。 每个客户服务 shell 都会打印以下内容 −
Querying customer for id with: 1 Querying customer for id with: 1
这实际上意味着请求是循环的。
配置 Spring 负载均衡器
我们可以配置负载均衡器来改变算法的类型,也可以提供自定义算法。 让我们看看如何调整我们的负载均衡器,以便为请求选择相同的客户端。
For that purpose, let us update our Feign Client to contain load balancer definition.
package com.tutorialspoint;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "customer-service")
@LoadBalancerClient(name = "customer-service",
configuration=LoadBalancerConfiguration.class)
public interface CustomerService {
@RequestMapping("/customer/{id}")
public Customer getCustomerById(@PathVariable("id") Long id);
}
如果你注意到了,我们已经添加了 @LoadBalancerClient 注解,它指定了用于这个 Feign 客户端的负载均衡器的类型。我们可以为负载均衡器创建一个配置类,并将该类传递给注解本身。 现在让我们定义 LoadBalancerConfiguratio.java
package com.tutorialspoint;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier
discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
System.out.println("Configuring Load balancer to prefer same instance");
return ServiceInstanceListSupplier.builder()
.withBlockingDiscoveryClient()
.withSameInstancePreference()
.build(context);
}
}
现在,如您所见,我们已将客户端负载平衡设置为每次都使用相同的实例。 现在我们已经完成了设置,让我们试一试。 这里只是一点背景,我们要做的是以下 −
启动 Eureka 服务。
启动 Customer 服务的两个实例。
启动内部调用客户服务并使用 Spring Cloud 负载均衡器的 Restaurant 餐厅服务
对 Restaurant 餐厅服务进行 4 次 API 调用。 理想情况下,所有四个请求都将由相同的客户服务提供。
假设我们已经启动了 Eureka 服务器和客户服务实例,现在让我们编译餐厅服务代码,现在使用以下命令执行 −
java -Dapp_port=8082 -jar .\target\spring-cloud-feign-client-1.0.jar
现在,让我们通过访问以下 API http://localhost:8082/restaurant/customer/1 为位于 DC 的 Jane 找到餐馆,然后让我们再次访问相同的 API 三次。您会从客户服务的日志中注意到,单个实例服务于所有 4 个请求 −
Querying customer for id with: 1 Querying customer for id with: 1 Querying customer for id with: 1 Querying customer for id with: 1
这实际上意味着请求首选相同的客户服务代理。
在类似的线路上,我们可以有各种其他负载平衡算法来使用粘性会话、基于提示的负载平衡、区域偏好负载平衡等。

