前言
如果想要实现子服务之间的远程调用,需要通过RestTemplate,这是一个比较原始的方式,因为存在硬编码的特点:
一旦服务者地址变化,就需要手动修改代码;
一旦是多个服务(商品服务)提供者,无法实现负载均衡功能;
一旦服务变多,人工维护调用关系困难
那么应该怎么解决呢, 这时候就需要通过注册中心动态的实现服务治理。
但是如果想要发挥微服务动态扩缩容、集群部署的能力,就必须引入负载均衡
如下是RestTemplate实现的“硬编码”代码:
import com.example.service.OrderService;
import org.example.entity.Orders;
import org.example.entity.Product;
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;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@RequestMapping("/list")
public Object getList(){
List<Orders> list = orderService.list();
return list;
}
@RequestMapping("/insertOrder/{pid}")
public Object findProduct(@PathVariable("pid") Integer pid){
// 模仿实现订单的添加
// 创建订单对象
Orders order = new Orders();
// 设置用户信息
order.setUid(1);
order.setUsername("admin");
// 设置商品信息
// 使用RestTemplate来实现调用商品微服务的接口
Product product = restTemplate.getForObject("http://localhost:8081/product/findProduct/" + pid, Product.class);
order.setPid(product.getId());
order.setPname(product.getName());
order.setPprice(product.getPrice());
order.setNumber(1);
boolean i = orderService.save(order);
return i;
}
}
一、微服务为什么需要负载均衡?
微服务架构最大优势之一:支持动态扩缩容。
- 扩容:电商大促、流量高峰期,单个商品服务实例无法承载海量请求,启动多个相同商品服务实例组成集群,分担流量;
- 缩容:流量低谷期,关闭多余实例,节约服务器资源。
集群部署之后,引出核心问题:
服务消费者(订单服务)发起请求时,众多服务提供者实例,到底选择哪一台处理请求?
负载均衡,就是用来解决请求分发的核心方案。
二、什么是负载均衡
1. 定义
通俗理解:负载均衡就是把用户请求、任务流量,分摊到多个服务器实例上执行,避免单一节点压力过大,提升系统吞吐量与可用性。
2. 两大分类
根据负载均衡逻辑运行的位置,分为 服务端负载均衡、客户端负载均衡。

① 服务端负载均衡(集中式负载均衡)
代表组件:Nginx、Gateway 工作流程:
- 客户端只知道负载均衡中间件地址,请求统一发送到 Nginx;
- Nginx 保存后端所有服务实例清单;
- Nginx 按照负载均衡算法挑选一台后端服务器,转发请求;
- 服务处理完成,响应经由 Nginx 返回客户端。
特点:
- 负载均衡逻辑运行在独立中间件;
- 客户端不需要感知后端集群节点;
- 存在一次转发,有轻微网络开销;常用于网关层、反向代理。

② 客户端负载均衡(进程内负载均衡,微服务主流方案)
代表组件:Ribbon、SpringCloud LoadBalancer 工作流程:
- 服务消费者(订单服务)启动后,从注册中心 (Nacos) 拉取目标服务(商品服务)全部实例列表,缓存到本地内存;
- 负载均衡逻辑运行在消费者本地进程内部;
- 发起远程调用前,本地通过均衡算法选出一个服务实例;
- 消费者直接和选中的服务实例建立连接发起请求。
特点:
✅ 微服务之间远程调用首选方案;
✅ 不需要额外中间件转发,性能更好;
✅ 消费者本地缓存实例清单,实时接收注册中心推送的节点上下线消息;
SpringCloud Alibaba 微服务之间调用,使用的就是客户端负载均衡。

3.两种负载均衡对比:
| 类型 | 运行位置 | 典型框架 | 适用场景 |
|---|---|---|---|
| 服务端负载均衡 | 独立中间件(Nginx) | Nginx、Spring Cloud Gateway | 前端请求接入、网关入口 |
| 客户端负载均衡 | 服务调用方内部 | Ribbon、LoadBalancer | 微服务与微服务之间内部调用 |
三、自定义实现负载均衡
1.实现思路
- 借助
DiscoveryClient从 Nacos 注册中心,拉取商品服务所有可用实例集合; - 自定义负载均衡算法(轮询、随机),从实例列表挑选一个节点;
- 拼接选中实例的 IP + 端口,构建完整 URL 发起远程调用。
2.代码示例(简易轮询负载均衡)
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/insertOrder/{pid}")
public Object findProduct(@PathVariable("pid") Integer pid){
// 使用nacos方式来动态的获取要访问的服务器
List<ServiceInstance> instances = discoveryClient.getInstances("shop- product");
int index = new Random().nextInt(instances.size()); //自定义负载均衡,生成一个随机数,随机获取服务
ServiceInstance serviceInstance = instances.get(index);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
//通过restTemplate调用商品微服务
Product product = restTemplate.
getForObject("http://" + url + "/product/findProduct/" + pid, Product.class);
System.out.println(product.toString());
// 模仿实现订单的添加
// 创建订单对象
Orders order = new Orders();
// 设置用户信息
order.setUid(1);
order.setUsername("admin");
// ?? 设置商品信息 ??
order.setPid(product.getId());
order.setPname(product.getName());
order.setPprice(product.getPrice());
order.setNumber(1);
boolean i = orderService.save(order);
return i;
}
}
手写方案存在的缺陷:
- 代码侵入严重,每个接口都需要重复编写实例获取、算法逻辑;
- 需要自己处理服务节点下线、实例列表刷新;
- 切换负载均衡算法需要大面积修改代码;
- 缺少重试、超时、故障转移机制。
企业开发不会手写负载均衡,该方案仅用于理解原理。 生产环境直接使用成熟组件 Ribbon。
四、基于Ribbon实现负载均衡
1. Ribbon 介绍
Ribbon 是 Netflix 开源的客户端负载均衡组件,是 SpringCloud 初代负载均衡标准实现。
小贴士:Spring Cloud 2020 版本之后官方使用 Spring Cloud LoadBalancer 替代 Ribbon; 但 Spring Cloud Alibaba 2021.0.1.0 体系中,RestTemplate 配合
@LoadBalanced底层依旧兼容 Ribbon,也是现阶段学习主流方案。
2. 快速改造实现负载均衡
步骤 1:配置类给 RestTemplate 添加 @LoadBalanced注解
@Configuration
public class RestTemplateConfig {
// @LoadBalanced:开启Ribbon客户端负载均衡能力
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
步骤 2:修改调用 URL,不再硬编码 IP 端口,使用服务名称调用
// 修改前(硬编码地址,不支持集群)
// String url = "http://localhost:8081/product/findProduct/" + pid;
// 修改后:直接填写Nacos注册的服务名 shop_product
String url = "http://shop_product/product/findProduct/" + pid;
Product product = restTemplate.getForObject(url, Product.class);
3. 运行原理剖析
- 订单服务启动,
@LoadBalanced对 RestTemplate 进行增强; - 发起请求
http://shop_product/xxx时,拦截器识别这是服务名; - 从 Nacos 拉取
shop_product全部实例列表; - Ribbon 内置负载均衡算法挑选一个实例;
- 将服务名替换成真实 IP:port,发起 http 请求。
4. Ribbon 内置负载均衡策略
| 策略 | 说明 |
|---|---|
| RoundRobinRule(默认) | 轮询策略,请求依次分发到每一个实例 |
| RandomRule | 随机选择实例 |
| WeightedResponseTimeRule | 根据实例响应时间分配权重,响应越快权重越高 |
| BestAvailableRule | 选择当前并发请求量最小的实例 |
总结
- 负载均衡分为服务端负载均衡 (Nginx) 和客户端负载均衡 (Ribbon),微服务内部调用优先使用客户端负载均衡;
- 客户端负载均衡核心:调用方本地缓存服务实例清单,调用前本地完成节点选择;
- 手写负载均衡帮助理解原理,正式项目使用 Ribbon;
- 使用 Ribbon 核心两点:
@LoadBalanced注解 + URL 中使用注册中心服务名称替代硬编码 IP 端口; - 负载均衡配合 Nacos 服务注册发现,彻底解决地址硬编码、服务集群流量分发两大痛点,支撑微服务弹性扩缩容。


被折叠的 条评论
为什么被折叠?



