6人参与 • 2026-03-18 • Java
loadbalancer(负载均衡器)是一种用来分发网络或应用程序流量到多个服务器的技术。
它可以防止任何单一服务的过载,通过分散负载来保持整个系统的平稳运行,保证系统的高可用性和可靠性。
负载均衡策略大体上分为两类:服务端的负载均衡和客户端的负载均衡
① 服务端负载均衡 (如 nginx、f5)
请求先到达一个中介(如负载均衡器设备或者服务,例如nginx),由这个中介根据配置的策略将请求分发到后端的多个服务器中。它对客户端是透明的,即客户端不需要知道有多少服务器以及它们的存在。

② 客户端负载均衡 (如 ribbon、spring cloud loadbalancer)
请求的分配逻辑由客户端持有,客户端直接决定将请求发送到哪一个服务器。也就是说在客户端负载均衡中,客户端通常具备一份服务列表,它知道每个服务的健康状况,基于这些信息和负载均衡策略,客户端会选择一个最适合的服务去发送请求。

服务端负载均衡和客户端负载均衡策略有什么区别 ?
它俩的区别主要在灵活性和性能两方面(结合上面两幅图来理解):
1. 灵活性
2. 性能
【扩充知识】
如果将负载均衡器视为代理,那么服务端负载均衡可以视作是反向代理的一种形式,因为它接收客户端请求后再决定将其分配给哪一个服务器;而客户端负载均衡则可以看作具有正向代理的性质,因为客户端知道要联系的服务列表,并直接向选定的服务器发送请求。
常见的负载均衡策略有以下几种:
因为 ribbon 作为早期的客户端负载均衡工具,在 spring cloud 2020.0.0 版本之后已经被移除了,取而代之的是 spring cloud loadbalancer,而且 ribbon 也已经不再维护,所以它也是 spring 官方推荐的负载均衡解决方案。
其他一些原因:
从它的源码实现可以看出来默认的负载均衡策略是轮询的策略。
idea 搜索它的配置类 loadbalancerclientconfiguration:

进入到 roundrobinloadbalancer 这个类里边,定位到 getinstanceresponse 方法,就能看到轮询策略的关键代码:
private response<serviceinstance> getinstanceresponse(list<serviceinstance> instances) {
if (instances.isempty()) {
if (log.iswarnenabled()) {
log.warn("no servers available for service: " + this.serviceid);
}
return new emptyresponse();
} else if (instances.size() == 1) {
return new defaultresponse((serviceinstance)instances.get(0));
} else {
// 轮询策略的关键代码
int pos = this.position.incrementandget() & integer.max_value;
serviceinstance instance = (serviceinstance)instances.get(pos % instances.size());
return new defaultresponse(instance);
}
}理解关键代码:
int pos = this.position.incrementandget() & integer.max_value;
this.position.incrementandget() 方法等价于 "++随机数 "。这是一个原子操作,保证了每次调用都会得到一个唯一的递增数值。& integer.max_value 这部分是一个位运算,它确保了如果 position 的值增加到超过 integer.max_value 时,不会产生负数。其一,在轮询算法中,如果计数器变成负数,那么取余操作可能会产生负的索引值,这是无效的; 其二,也可也保证在相同规则底下的公平性。serviceinstance instance = (serviceinstance)
instances.get(pos % instances.size()); // 进行轮询选择instances 是一个包含所有服务实例的列表。pos % instances.size() 计算的是 pos 除以 instances 列表大小的余数,这保证了不论 pos 增长到多大,这个表达式的结果都是在 0 到 instances.size() - 1 的范围内,这样就可以循环地从服务实例列表中选择服务实例。实现随机负载均衡策略的步骤:
① 创建随机负载均衡策略
② 设置随机负载均衡策略
接下来的操作都是基于这篇博客基础上去操作的,有需要的可以先去看看这篇博客,先把前置的代码准备好:https://www.jb51.net/program/360525nc3.htm
这些写法都是相通的,可以仿照源码中的轮询策略的关键代码:
可以去源码中的loadbalancerclientconfiguration中去定位到 reactorserviceinstanceloadbalancer 方法,然后复制下来,修改几个关键地方即可。
public class randomloadbalancerconfig {
// 随机的负载均衡策略
@bean
public reactorloadbalancer<serviceinstance> randomloadbalancer(environment environment, loadbalancerclientfactory loadbalancerclientfactory) {
string name = environment.getproperty("loadbalancer.client.name");
return new randomloadbalancer(
loadbalancerclientfactory.getlazyprovider(name,
serviceinstancelistsupplier.class), name);
}
}在 consumer 模块中的 service 接口上设置负载均衡策略:
@service
@feignclient("loadbalancer-service")
// 设置局部的负载均衡策略
@loadbalancerclient(name = "loadbalancer-service",
configuration = randomloadbalancerconfig.class)
public interface userservice {
@requestmapping("/user/getname")
public string getname(@requestparam("id") integer id);
}
ps:有时候局部的负载均衡策略不会生效(版本问题),可以将其设为全局的负载均衡策略。
如何设置全局的负载均衡策略:(在启动类上加 @loadbalancerclients 注解)
@springbootapplication
@enablefeignclients // 开启 openfeign
// 设置全局的负载均衡策略
@loadbalancerclients(defaultconfiguration =
randomloadbalancerconfig.class)
public class consumerapplication {
public static void main(string[] args) {
springapplication.run(consumerapplication.class, args);
}
}
这个时候,就是随机的负载均衡策略了,可以启动两个生产者和消费者,然后拿着消费者这边的端口去获取服务感受。
nacos 中有两种负载均衡策略:权重负载均衡策略和 cmdb(地域就近访问)标签负载均衡策略
它默认的策略是权重。
在 spring cloud balancer 配置为 nacos 负载均衡器的步骤:
① 创建 nacos 负载均衡器
② 设置 nacos 负载均衡器
配置 nacos 负载均衡需要注入nacosdiscoveryproperties 这个类,因为它需要使用到配置文件中的一些关键信息。
@loadbalancerclients(defaultconfiguration = nacosloadbalancerconfig.class)
public class nacosloadbalancerconfig {
@resource
nacosdiscoveryproperties nacosdiscoveryproperties;
@bean
public reactorloadbalancer<serviceinstance> nacosloadbalancer(environment environment, loadbalancerclientfactory loadbalancerclientfactory) {
string name = environment.getproperty("loadbalancer.client.name");
return new nacosloadbalancer(
loadbalancerclientfactory.getlazyprovider(name,
serviceinstancelistsupplier.class), name, nacosdiscoveryproperties);
}
}
@service
@feignclient("loadbalancer-service")
// 设置局部的负载均衡策略
@loadbalancerclient(name = "loadbalancer-service",
configuration = nacosloadbalancerconfig.class)
public interface userservice {
@requestmapping("/user/getname")
public string getname(@requestparam("id") integer id);
}
再测试之前,可以先将 nacos 中一个生产者的权重给设置为 10,一个设置为 1,这样就能明显感受到 nacos 权重的负载均衡策略了。

自定义负载均衡策略需要 3 个步骤:
① 创建自定义负载均衡器
② 封装自定义负载均衡器
③ 为服务设置自定义负载均衡策器
这里也是可以参考源码的实现的,搜索randomloadbalancer 这个类,模仿它的实现去创建自定义负载均衡器。
ⅰ. 创建一个负载均衡类,并让其实现reactorserviceinstanceloadbalancer 接口;
ⅱ. 复制randomloadbalancer 的整个方法体,粘贴到自定义负载均衡类中,并修改构造方法名称
ⅲ. 在关键方法 getinstanceresponse 中实现自定义负载均衡策略(以ip哈希负载均衡为例)
public class customloadbalancer implements reactorserviceinstanceloadbalancer {
private static final log log = logfactory.getlog(customloadbalancer.class);
private final string serviceid;
private objectprovider<serviceinstancelistsupplier> serviceinstancelistsupplierprovider;
public customloadbalancer(objectprovider<serviceinstancelistsupplier> serviceinstancelistsupplierprovider, string serviceid) {
this.serviceid = serviceid;
this.serviceinstancelistsupplierprovider = serviceinstancelistsupplierprovider;
}
public mono<response<serviceinstance>> choose(request request) {
serviceinstancelistsupplier supplier = (serviceinstancelistsupplier)this.serviceinstancelistsupplierprovider.getifavailable(noopserviceinstancelistsupplier::new);
return supplier.get(request).next().map((serviceinstances) -> {
return this.processinstanceresponse(supplier, serviceinstances);
});
}
private response<serviceinstance> processinstanceresponse(serviceinstancelistsupplier supplier, list<serviceinstance> serviceinstances) {
response<serviceinstance> serviceinstanceresponse = this.getinstanceresponse(serviceinstances);
if (supplier instanceof selectedinstancecallback && serviceinstanceresponse.hasserver()) {
((selectedinstancecallback)supplier).selectedserviceinstance((serviceinstance)serviceinstanceresponse.getserver());
}
return serviceinstanceresponse;
}
private response<serviceinstance> getinstanceresponse(list<serviceinstance> instances) {
if (instances.isempty()) {
if (log.iswarnenabled()) {
log.warn("no servers available for service: " + this.serviceid);
}
return new emptyresponse();
} else {
// 自定义负载均衡策略
// 获取 request 对象
servletrequestattributes attributes = (servletrequestattributes)
requestcontextholder.getrequestattributes();
httpservletrequest request = attributes.getrequest();
string ipaddress = request.getremoteaddr();
system.out.println("用户 ip:" + ipaddress);
int hash = ipaddress.hashcode();
// ip 哈希负载均衡【关键代码】
int index = hash % instances.size();
// 得到服务实例的方法
serviceinstance instance = (serviceinstance) instances.get(index);
return new defaultresponse(instance);
}
}
}
public class customloadbalancerconfig {
// ip 哈希负载均衡
@bean
public reactorloadbalancer<serviceinstance> customloadbalancer(environment environment, loadbalancerclientfactory loadbalancerclientfactory) {
string name = environment.getproperty("loadbalancer.client.name");
return new customloadbalancer(
loadbalancerclientfactory.getlazyprovider(name,
serviceinstancelistsupplier.class), name);
}
}
@service
@feignclient("loadbalancer-service")
// 设置局部的负载均衡策略
@loadbalancerclient(name = "loadbalancer-service",
configuration = customloadbalancerconfig.class)
public interface userservice {
@requestmapping("/user/getname")
public string getname(@requestparam("id") integer id);
}
ps:测试的时候发现自定义的负载均衡策略不生效怎么办 ?
① 把前边的 nacos 的负载均衡器一整个注释掉(包括 @loadbalancerclients注解),只提供一个类。

② 如果设置局部的负载均衡不生效,就去启动类上设置全局的负载均衡策略。
@springbootapplication
@enablefeignclients // 开启 openfeign
// 设置全局的负载均衡策略
@loadbalancerclients(defaultconfiguration =
customloadbalancerconfig.class)
public class consumerapplication {
public static void main(string[] args) {
springapplication.run(consumerapplication.class, args);
}
}
spring cloud loadbalancer 中获取服务实例有两种方式:
1. 实时获取:每次都从注册中心得到最新的健康实例(效果好,开销大)
2. 缓存服务列表:每次得到服务列表之后,缓存一段时间(既保证性能,也能保证一定的及时性)
spring cloud loadbalancer 默认开启了缓存服务列表的功能。
测试 spring cloud loadbalancer 的缓存机制:

【测试结果】 当我下线第一个服务的时候,立马去获取服务,这个时候还是两个服务轮询的获取,等过了 35s 左右,就只能获取到 64067 这个服务了。
默认特性如下:
① 缓存的过期时间为 35s;
② 缓存保存个数为 256 个。
我们可以通过在配置文件中去设置这些特性:
spring:
cloud:
loadbalancer:
cache:
ttl: 35s # 过期时间
capacity: 1024 # 设置缓存个数关闭 spring cloud loadbalancer 中的缓存可以通过以下配置文件来设置:
spring:
cloud:
loadbalancer:
cache:
enabled: false # 关闭缓存ps:尽管关闭缓存对于开发和测试很有用,但是在生产环境上,它的效率是要远低于开启缓存,所以在生产环境上始终都要开启缓存。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论