前文中已经介绍了服务中心 Nacos 的相关知识点,包括环境搭建、服务注册、服务发现以及源码分析,也结合实际的编码实现了纳入 Nacos 服务中心后的服务通信功能。与 Nacos 体系相关的内容讲解,暂时就告一段落了,在后续的补充章节中会继续讲解 Nacos 的其它功能点和进阶知识。本章节将介绍微服务架构中的一个新知识点——负载均衡器。
认识负载均衡
在 Nacos 服务注册的编码实现章节中,讲解了服务发现的原理并借助它来获取可用的服务实例信息,然后将服务名称转换为可用的请求地址并发起服务通信。在这个环节里,其实是有一个问题需要读者认真思考一下的。在获取可用的服务实例信息时读取的是 serviceInfoMap 变量,在这个对象中存储的服务实例信息是一个 List 列表对象,字段的存储结构如下图所示。
比如名称为 “newbee-cloud-goods-service” 的服务,就包括 192.168.1.101:9009、192.168.1.102:9009、192.168.1.103:9009 三个服务实例的信息。那么,在程序运行时通过服务发现机制获取到的可用实例信息就有三条,但是最终结果肯定是向其中的某一个实例发送请求。注意,是一个实例,并不是向三个实例都发起请求。此时,服务通信过程中是怎么实现选择目标实例的呢?
这就引出了接下来两个章节要介绍的知识点——微服务架构中的负载均衡器,笔者将结合实际的编码和源码分析来介绍。首先来介绍一下负载均衡的定义,如下所示:
负载均衡,英文名称为 Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如 FTP 服务器、Web 服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。
负载均衡构建在原有网络结构之上,它提供了一种透明且廉价有效的方法扩展服务器和网络设备的带宽、加强网络数据处理能力、增加吞吐量、提高网络的可用性和灵活性。
简单来说,负载均衡就是把请求根据规则分摊到集群中的不同服务器上。
提升系统的吞吐量,避免单点的问题。后端开发人员对负载均衡肯定不会陌生,常见的负载均衡软件是 Nginx,还有 Haproxy、Apache 等软件也提供负载均衡功能,常见的负载均衡 + 集群的部署简图如下:
不过,在 Nacos 服务注册的编码实现章节中,可并没有引入 Nginx 或者类似的负载均衡软件。查看代码可以发现,笔者只是在 pom.xml 文件中添加了 spring-cloud-starter-loadbalancer 依赖并且在 RestTemplate 类中添加了一个 @LoadBalance 的注解。这就是另外一种负载均衡的实现方案了。
Spring Cloud LoadBalancer 介绍
引入的 spring-cloud-starter-loadbalancer 依赖对应的负载均衡方案,就是本章节的主角——Spring Cloud LoadBalancer。
LoadBalancer 是负载均衡器 Ribbon 的替代方案。在 Spring Cloud Alibaba 技术体系介绍一章中,也介绍过 Spring Cloud 的主流套件,经历过一段时间的甜蜜期后,Netflix 撂挑子了。Spring Cloud 2020 版本以后,默认移除了对 Netflix 的依赖,其中就包括 Ribbon。现在 Spring Cloud 官方推荐使用 Loadbalancer 正式替换 Ribbon,它现在也是 Spring Cloud 体系中负载均衡器的唯一实现。
与 Nginx、Haproxy 等软件不同。不管是 Ribbon 还是 LoadBalancer,它们都只是一个负载均衡器,或者说是一个依赖包,必须要集成在服务实例中,甚至可以把它们看作是某个 Spring Boot 项目中的一个功能模块。它们并不是一个独立的软件,也不需要独立部署。当然,也有种说法把 Ribbon 和 LoadBalancer 这种方式称作 “客户端服务均衡”。项目中只需要添加相关的依赖,在项目运行期间自己就完成了负载均衡的工作,根据某种负载均衡策略选择一个可用的目标实例进行访问,不需要借助其它额外的软件。
这种方案有什么优点呢?
-
减少整个系统的复杂度,不需要额外部署负载均衡软件。
-
可以减少不必要的网络开销,因为请求不需要额外经过 Nginx 等负载均衡软件的一层转发。可用的目标实例信息都存储在本实例的 serviceInfoMap 变量中,选择其中一个直接发起请求即可。
Spring Cloud LoadBalancer 中内置的负载均衡规则实现类如下:
类名 | 说明 | 是否默认 |
---|---|---|
org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer | 轮询算法 | 是 |
org.springframework.cloud.loadbalancer.core.RandomLoadBalancer | 随机算法 | 否 |
LoadBalancer 只提供了两种基础的负载均衡算法。虽然现在 Spring Cloud 官方推荐使用 Loadbalancer 替代 Ribbon,但是也不得不吐槽一下 Loadbalancer 有些小家子气,与 Ribbon 默认提供的七种负载均衡算法相比,确实有些相形见绌。好在开发者们可以自行定义负载均衡算法,这个知识点笔者会在下一章进行介绍。
负载均衡器的功能演示
接下来,笔者将结合实际的代码来演示负载均衡器的作用和效果。
本节代码将在服务发现章节的源码基础上进行修改。因为是编写与 LoadBalancer 相关的代码,这里就把模板项目 spring-cloud-alibaba-nacos-demo 的名称改为 spring-cloud-alibaba-load-balance-demo,root 节点的 pom.xml 文件内容也修改掉。然后复制 nacos-provider-demo,分别命名为 nacos-provider-demo2 和 nacos-provider-demo3。
root 节点中 pom.xml 的文件内容最终如下所示:
<modelVersion>4.0.0</modelVersion>
<groupId>ltd.newbee.cloud</groupId>
<artifactId>spring-cloud-alibaba-load-balance-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-alibaba-load-balance-demo</name>
<packaging>pom</packaging>
<description>Spring Cloud Alibaba Load Balance Demo</description>
<modules>
<module>nacos-provider-demo</module>
<module>nacos-provider-demo2</module>
<module>nacos-provider-demo3</module>
<module>nacos-consumer-demo</module>
</modules>
最终的目录结构如下所示:
为了与其它章节做一个区分,把各个 Module 中 application.properties 配置文件中的启动端口号也进行了一些简单的修改。nacos-consumer-demo 中的 REST 测试类也做了修改,代码如下:
package ltd.newbee.cloud.api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class LoadBalancerTestController {
@Resource
private RestTemplate restTemplate;
private final String SERVICE_URL = "http://newbee-cloud-goods-service";
@GetMapping("/loadBalancerTest")
public String loadBalancerTest() {
return restTemplate.getForObject(SERVICE_URL + "/goodsServiceTest", String.class);
}
}
接下来,需要启动 Nacos Server,然后依次启动这 4 个项目。如果未能成功启动,开发者需要查看控制台中的日志是否报错,并及时确认问题和修复。启动成功后进入 Nacos 控制台,点击 “服务管理” 中的服务列表,可以看到列表中已经存在三条 newbee-cloud-goods-service 的服务信息和一条 newbee-cloud-goods-service-consumer 的信息,如下图所示。如果实例的数量不对,读者也需要检查是哪里出了问题。
然后,打开浏览器验证负载均衡器的功能,在地址栏输入如下地址:
http://localhost:8105/loadBalancerTest
第一次访问后,结果如下图所示。
此时得到的结果是 8101 实例上返回的。重复访问会依次得到 8102 实例和 8103 实例上的数据响应,如下图所示。
后续的重复请求中,也会按照这个顺序依次轮训下去,然后分别获得这三个实例上的数据响应。当然,读者在进行功能测试时,得到的顺序可能和笔者演示的不同。笔者在做测试时得到的轮训顺序是 8101→8102→8103,读者在进行功能演示时获取到的顺序可能是 8102→8103→8101,8103→8102→8101,这个是正常现象。但是一旦顺序确定,之后就是一直以一个顺序轮训下去了。功能演示完成。
总结
结合前面几个章节,这里做一次知识点的总结和归纳:
-
首先,服务化之后需要一个服务中心来进行服务治理,所以介绍了 Nacos 以及服务注册的相关知识点。
-
服务化后的通信机制是通过服务名称来完成调用,但是服务名称不是一个可用的请求地址,必须要进行由服务名称到 HTTP 请求地址的转换,此时就需要服务发现机制加入进来。不然,没有实例信息也无法进行 HTTP 地址的计算和转换。
-
接下来,服务实例信息获取了,但是一个服务名称可能对应多个可用的服务实例。地址太多了怎么办?此时就需要一个工具来做筛选。好的,本章节所介绍的负载均衡器就加入进来了。
现在,读者应该能够理清这里面的关联和知识点的必要性了吧。当然,读者如果有任何问题或者想要和笔者讨论的内容,都可以在评论区留下看法,笔者会根据读者的反馈和问题继续整理和完善本章节内容。下一章,笔者将结合源码来分析负载均衡器的原理,并结合编码来讲解如何自定义一个负载均衡算法。
原文地址 juejin.cn