1. Introduction
1.绪论
As microservice architectures become more popular, it’s becoming more common to run multiple services distributed across different servers. In this quick tutorial, we’ll look at using Spring Cloud Load Balancer to create more fault-tolerant applications.
随着微服务架构的普及,分布在不同服务器上的多个服务的运行也变得越来越普遍。在本快速教程中,我们将了解如何使用Spring Cloud Load Balancer来创建更具容错性的应用程序。
2. What Is Load Balancing?
2.什么是负载平衡?
Load balancing is the process of distributing traffic among different instances of the same application.
负载均衡是在同一应用的不同实例之间分配流量的过程。
To create a fault-tolerant system, it’s common to run multiple instances of each application. Thus, whenever one service needs to communicate with another, it needs to pick a particular instance to send its request.
为了创建一个容错的系统,通常要为每个应用程序运行多个实例。因此,每当一个服务需要与另一个服务进行通信时,它需要挑选一个特定的实例来发送其请求。
There are many algorithms when it comes to load balancing:
谈到负载平衡,有许多算法。
- Random selection: Choosing an instance randomly
- Round-robin: Choosing an instance in the same order each time
- Least connections: Choosing the instance with the fewest current connections
- Weighted metric: Using a weighted metric to choose the best instance (for example, CPU or memory usage)
- IP hash: Using the hash of the client IP to map to an instance
These are just a few examples of load balancing algorithms, and each has its pros and cons.
这些只是负载平衡算法的几个例子,而且每一种都有其优点和缺点。
Random selection and round-robin are easy to implement but may not optimally use services. Conversely, the least connections and weighted metrics are more complex but generally create more optimal service utilization. And IP hash is great when server stickiness is important, but it isn’t very fault-tolerant.
随机选择和轮换很容易实现,但可能无法最佳地利用服务。相反,最少连接和加权指标更复杂,但通常会创造更多的最佳服务利用率。而当服务器的粘性很重要时,IP哈希是很好的,但它的容错性并不高。
3. Introduction to Spring Cloud Load Balancer
3.Spring Cloud Load Balancer简介
The Spring Cloud Load Balancer library allows us to create applications that communicate with other applications in a load-balanced fashion. Using any algorithm we want, we can easily implement load balancing when making remote service calls.
Spring Cloud Load Balancer库允许我们创建以负载平衡方式与其他应用程序通信的应用程序。使用我们想要的任何算法,我们可以在进行远程服务调用时轻松实现负载平衡。
To illustrate, let’s look at some example code. We’ll start with a simple server application. The server will have a single HTTP endpoint and can be run as multiple instances.
为了说明这一点,让我们看一下一些示例代码。我们将从一个简单的服务器应用程序开始。该服务器将有一个单一的HTTP端点,可以作为多个实例运行。
Then, we’ll create a client application that uses Spring Cloud Load Balancer to alternate requests between different instances of the server.
然后,我们将创建一个客户端应用程序,使用Spring Cloud Load Balancer在服务器的不同实例之间交替进行请求。
3.1. Example Server
3.1.服务器实例
For our example server, we start with a simple Spring Boot application:
对于我们的示例服务器,我们从一个简单的Spring Boot应用程序开始。
@SpringBootApplication
@RestController
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
@Value("${server.instance.id}")
String instanceId;
@GetMapping("/hello")
public String hello() {
return String.format("Hello from instance %s", instanceId);
}
}
We start by injecting a configurable variable named instanceId. This allows us to differentiate between multiple running instances. Next, we add a single HTTP GET endpoint that echoes back a message and instance ID.
我们首先注入一个名为instanceId.的可配置变量,这使得我们能够区分多个运行中的实例。接下来,我们添加一个HTTP GET端点,回传一个消息和实例ID。
The default instance will run on port 8080 with an ID of 1. To run a second instance, we just need to add a couple of program arguments:
默认的实例将在8080端口运行,ID为1。 要运行第二个实例,我们只需要添加几个程序参数。
--server.instance.id=2 --server.port=8081
3.2. Example Client
3.2.客户端实例
Now, let’s look at the client code. This is where we use Spring Cloud Load Balancer, so let’s start by including it in our application:
现在,让我们来看看客户端的代码。这就是我们使用Spring Cloud Load Balancer的地方,所以让我们先把它纳入我们的应用程序。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Next, we create an implementation of ServiceInstanceListSupplier. This is one of the key interfaces in Spring Cloud Load Balancer. It defines how we find available service instances.
接下来,我们创建一个ServiceInstanceListSupplier的实现。这是Spring Cloud Load Balancer的关键接口之一。它定义了我们如何找到可用的服务实例。
For our sample application, we’ll hard-code two different instances of our example server. They run on the same machine but use different ports:
对于我们的示例应用程序,我们将对我们的示例服务器的两个不同实例进行硬编码。它们在同一台机器上运行,但使用不同的端口。
class DemoInstanceSupplier implements ServiceInstanceListSupplier {
private final String serviceId;
public DemoInstanceSupplier(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", 8080, false),
new DefaultServiceInstance(serviceId + "2", serviceId, "localhost", 8081, false)));
}
}
In a real-world system, we would want to use an implementation that does not hard-code service addresses. We’ll look at this a little more later on.
在一个真实世界的系统中,我们希望使用一个不对服务地址进行硬编码的实现。我们将在后面再看一下这个问题。
Now, let’s create a LoadBalancerConfiguration class:
现在,让我们创建一个LoadBalancerConfiguration类。
@Configuration
@LoadBalancerClient(name = "example-service", configuration = DemoServerInstanceConfiguration.class)
class WebClientConfig {
@LoadBalanced
@Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
This class has one role: create a load-balanced WebClient builder to make remote requests. Notice that our annotation uses a pseudo name for the service.
这个类有一个作用:创建一个负载平衡的WebClient构建器来进行远程请求。注意,我们的注解为服务使用了一个伪名称。
This is because we likely won’t know the actual hostnames and ports for running instances ahead of time. So, we use a pseudo name as a placeholder, and the framework will substitute real values when it picks a running instance.
这是因为我们可能不会提前知道运行实例的实际主机名和端口。因此,我们使用一个伪名字作为占位符,框架在挑选运行实例时将用真实的值来代替。
Next, let’s create a Configuration class that instantiates our service instance supplier. Notice that we use the same pseudo name as above:
接下来,让我们创建一个Configuration类,将我们的服务实例供应商实例化。请注意,我们使用了与上面相同的伪装名称。
@Configuration
class DemoServerInstanceConfiguration {
@Bean
ServiceInstanceListSupplier serviceInstanceListSupplier() {
return new DemoInstanceSupplier("example-service");
}
}
Now, we can create the actual client application. Let’s use the WebClient bean from above to send ten requests to the example server:
现在,我们可以创建实际的客户端应用程序。让我们使用上面的WebClientbean来向示例服务器发送十个请求。
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(ClientApplication.class)
.web(WebApplicationType.NONE)
.run(args);
WebClient loadBalancedClient = ctx.getBean(WebClient.Builder.class).build();
for(int i = 1; i <= 10; i++) {
String response =
loadBalancedClient.get().uri("http://example-service/hello")
.retrieve().toEntity(String.class)
.block().getBody();
System.out.println(response);
}
}
}
Looking at the output, we can confirm that we’re load balancing between two different instances:
看一下输出,我们可以确认,我们正在两个不同的实例之间进行负载平衡。
Hello from instance 2
Hello from instance 1
Hello from instance 2
Hello from instance 1
Hello from instance 2
Hello from instance 1
Hello from instance 2
Hello from instance 1
Hello from instance 2
Hello from instance 1
4. Other Features
4.其他特点
The example server and client show a very simple use of Spring Cloud Load Balancer. But other library features are worth mentioning.
服务器和客户端的例子展示了Spring Cloud Load Balancer的一个非常简单的用途。但其他库的功能也值得一提。
For starters, the example client used the default RoundRobinLoadBalancer policy. The library also provides a RandomLoadBalancer class. We could also create our own implementation of ReactorServiceInstanceLoadBalancer with any algorithm we want.
首先,示例客户端使用默认的RoundRobinLoadBalancer策略。该库还提供了一个RandomLoadBalancer类。我们也可以用我们想要的任何算法创建我们自己的ReactorServiceInstanceLoadBalancer的实现。
Additionally, the library provides a way to discover service instances dynamically. We do this using the DiscoveryClientServiceInstanceListSupplier interface. This is useful for integrating with service discovery systems such as Eureka or Zookeeper.
此外,该库还提供了一种方法来动态发现服务实例。我们使用DiscoveryClientServiceInstanceListSupplier接口来做到这一点。这对于与服务发现系统(如Eureka或Zookeeper)集成非常有用。
In addition to different load balancing and service discovery features, the library also offers a basic retry capability. Under the hood, it ultimately relies on the Spring Retry library. This allows us to retry failed requests, possibly using the same instance after some waiting period.
除了不同的负载平衡和服务发现功能外,该库还提供了一个基本的重试功能。在引擎盖下,它最终依赖于Spring Retry库。这使我们能够重试失败的请求,可能在等待一段时间后使用同一实例。
Another built-in feature is metrics, which is built on top of the Micrometer library. Out of the box, we get basic service level metrics for each instance, but we can also add our own.
另一个内置功能是指标,它是建立在Micrometer库之上的。开箱即用,我们可以获得每个实例的基本服务水平指标,但我们也可以添加自己的指标。
Finally, the Spring Cloud Load Balancer library provides a way to cache service instances using the LoadBalancerCacheManager interface. This is important because, in reality, looking up available service instances likely involves a remote call. This means it can be expensive to lookup data that doesn’t change often, and it also represents a possible failure point in the application. By using a cache of service instances, our applications can work around some of these shortcomings.
最后,Spring Cloud Load Balancer库提供了一种使用LoadBalancerCacheManager接口来缓存服务实例的方法。这一点很重要,因为在现实中,查询可用的服务实例可能涉及到远程调用。这意味着查询不经常变化的数据的成本很高,而且这也是应用程序中可能出现的故障点。通过使用服务实例的缓存,我们的应用程序可以解决其中的一些缺陷。
5. Conclusion
5.总结
Load balancing is an essential part of building modern, fault-tolerant systems. Using Spring Cloud Load Balancer, we can easily create applications that use various load balancing techniques to distribute requests to different service instances.
负载均衡是构建现代容错系统的一个重要部分。使用Spring Cloud Load Balancer,我们可以轻松地创建使用各种负载平衡技术的应用程序,将请求分配给不同的服务实例。
And, of course, all of the example code here can be found over on GitHub.
当然,这里的所有示例代码都可以在GitHub上找到。