负载均衡 -LoadBalance

目录

问题分析

负载均衡

服务端负载均衡

客户端负载均衡

Spring Cloud LoadBalancer

自定义负载均衡策略

实现原理


问题分析

在 服务注册与发现——Eureka-ZEEKLOG博客 中,我们根据应用名称获取了服务实例列表,并从列表中选择了一个服务实例:

若一个服务对应多个实例,是否能够将流量合理的分配到多个实例呢?

我们启动多个 product-service 实例

修改端口号:

再添加一个实例,并启动:

观察 Eureka,可以看到 product-service 中有三个实例:

此时,我们多次访问 127.0.0.1:8080/order/1

可以看到,多次访问的都是同一台机器,我们启动多个实例,就是希望能够减轻单机压力,也就是每个实例处理部分请求,而不是让同一台机器处理所有请求

那么,如何实现多个机器分摊负荷呢?

我们可以依次将请求分发给服务器列表中的每一台机器,因此,我们对 OrderService 中代码进行修改:

@Slf4j @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; // 当前选择实例 private static AtomicInteger atomicInteger = new AtomicInteger(1); // 实例列表 private static List<ServiceInstance> instances; @PostConstruct public void init() { // 获取服务列表 instances = discoveryClient.getInstances("product-service"); } public OrderInfo findOrderInfoById(Integer orderId) { OrderInfo orderInfo = orderMapper.selectOrderById(orderId); // 计算当前访问实例 int index = atomicInteger.getAndIncrement() % instances.size(); EurekaServiceInstance instance = (EurekaServiceInstance)instances.get(index); log.info("选择实例: " + instance.getInstanceId()); // 拼接 URL String url = instance.getUri() + "/product/" + orderInfo.getProductId(); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; } }

 多次访问 127.0.0.1:8080/order/1

此时请求被均衡地分配到了不同实例上,上述这种方式,就是负载均衡

负载均衡

负载均衡(Load Balance,简称 LB)是高并发、高可用系统必不可少的关键组件,目的是将网络流量或计算任务智能地分配到多个服务器(或资源)从而 提高系统性能(响应更快)、增强可用性与可靠性(一台服务器宕机,其他还能继续服务)、提升可扩展性(通过增加服务器来应对更多请求)以及 避免单点过载(防止某台服务器被压垮)

我们通过一个生活中的示例来理解:

想象你有一家热门奶茶店,若只有一个收银员时,此时队伍会排得很长
而如果开了 3 个收银台,并有一个引导员把顾客平均分配到各窗口,整体效率就大幅提升 —— 这个“引导员”就是负载均衡器

负载均衡的核心作用

1. 分发请求:把用户请求(如 HTTP 请求)分给后端多个服务器。

2. 健康检查:自动检测服务器是否宕机,剔除故障节点。

3. 缓存与压缩:部分负载均衡器还能缓存静态内容,加速响应

常见负载均衡策略

策略

说明

轮询(Round Robin)

依次轮流分配请求(最简单常用)

加权轮询

性能强的服务器分配更多请求(如 A 权重 3,B 权重 1)

最少连接

把请求发给当前连接数最少的服务器

IP Hash

根据用户 IP 固定分配到某台服务器(实现会话保持)

响应时间优先

选择响应最快、负载最低的服务器

负载均衡的实现可分为 服务端负载均衡客户端负载均衡,这两种不同的流量分发策略核心区别在于:"谁来决定请求发给哪台后端服务器?"

服务端负载均衡

独立的负载均衡器(如 Nginx、云 LB) 位于客户端和后端服务之间,统一接收所有请求并分发到后端服务器

客户端负载均衡

客户端自己决定将请求发给哪一台后端服务器。客户端需事先获取服务实例列表(通常通过服务注册中心),并在本地执行负载均衡算法

接下来,我们来学习 Spring Cloud LoadBalance

Spring Cloud LoadBalancer

Spring Cloud LoadBalancer Spring Cloud 提供的一个客户端负载均衡器,用于在微服务架构中,让服务消费者(客户端)能够从多个服务实例中智能选择一个可用实例进行调用

在 Spring Cloud Netflix Ribbon 被弃用后,Spring Cloud LoadBalancer 成为了官方推荐的替代方案

要使用 Spring Cloud LoadBalancer 实现负载均衡策略非常简单,只需要给 RestTemplate 添加 @LoadBalanced 注解即可

@Configuration public class BeanConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }

IP 和端口号修改为服务名称

@Slf4j @Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; public OrderInfo findOrderInfoById(Integer orderId) { OrderInfo orderInfo = orderMapper.selectOrderById(orderId); // 访问 URL String url = "http://product-service/product/" + orderInfo.getProductId(); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; } } 

多次发起请求   127.0.0.1:8080/order/1,观察 product-service 的日志,可以看到请求被分配到这三个实例上:

Spring Cloud LoadBalancer 仅支持两种负载均衡策略:轮询策略随机策略,默认的负载均衡策略是 轮询策略RoundRobinLoadBalancer

若需要采用 随机策略,也非常简单

自定义负载均衡策略

定义随机策略对象,并通过 @Bean 将其加载到 Spring 容器中:

public class CustomLoadBalancerConfiguration { @Bean ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new RandomLoadBalancer(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class), name); } }

使用 @LoadBalancerClient 注解配置上述随机策略:

@LoadBalancerClient(name = "product-service", configuration = CustomLoadBalancerConfiguration.class) @Configuration public class BeanConfig { @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
name:配置的负载均衡策略对哪个服务生效

configuration:使用的负载均衡策略

多次发起请求   127.0.0.1:8080/order/1,观察 product-service 的日志:

此时请求分布接近均匀

那么 Spring Cloud LoadBalancer 具体是如何实现负载均衡的呢?接下来我们就来看 Spring Cloud LoadBalancer 的具体实现

实现原理

Spring Cloud LoadBalancer 主要是通过 LoadBalancerInterceptor 来实现的,LoadBalancerInterceptor 会对 RestTemplate 的 请求进行拦截,然后从 Eureka 根据服务 id 获取服务列表,最后根据负载均衡算法得到真实的服务地址信息,并替换服务 id

我们来看 LoadBalancerInterceptor 的具体实现:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { // 从请求中获取 url, 如 http://product-service/product/1001 final URI originalUri = request.getURI(); // 获取路径主机名, 也就是服务id, 即 product-service String serviceName = originalUri.getHost(); // 判断 serviceName 是否为空 Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); // 根据服务 id 进行负载均衡,并处理请求 return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); } } 

继续看 execute 实现:

public class BlockingLoadBalancerClient implements LoadBalancerClient { private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory; @Override public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { String hint = getHint(serviceId); LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(request, buildRequestContext(request, hint)); Set<LoadBalancerLifecycle> supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId); supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest)); // 根据 serviceId 和 lbRequest(负载均衡策略)选择服务 ServiceInstance serviceInstance = choose(serviceId, lbRequest); if (serviceInstance == null) { supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete( new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse()))); throw new IllegalStateException("No instances available for " + serviceId); } return execute(serviceId, serviceInstance, lbRequest); } @Override public <T> ServiceInstance choose(String serviceId, Request<T> request) { // 获取负载均衡器 ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId); if (loadBalancer == null) { return null; } // 根据负载均衡算法,从列表中选择一个服务实例 Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block(); if (loadBalancerResponse == null) { return null; } return loadBalancerResponse.getServer(); } } 

我们继续看不同负载均衡策略选择实现,先来看轮询策略RoundRobinLoadBalancer)实现:

public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer { @Override public Mono<Response<ServiceInstance>> choose(Request request) { ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider .getIfAvailable(NoopServiceInstanceListSupplier::new); // 通过 processInstanceResponse 进行处理 return supplier.get(request).next() .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { // 调用 getInstanceResponse 获取选择的实例 Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); } if (instances.size() == 1) { return new DefaultResponse(instances.get(0)); } // 原子性地增加并返回新值 同时 任何数字 & MAX_VALUE = 保留低31位,清除符号位 int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; // 将无限增长的 pos 映射到有限的实例范围内 ServiceInstance instance = instances.get(pos % instances.size()); return new DefaultResponse(instance); } }

再来看随机策略RandomLoadBalancer)实现:

public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { @Override public Mono<Response<ServiceInstance>> choose(Request request) { ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider .getIfAvailable(NoopServiceInstanceListSupplier::new); // 通过 processInstanceResponse 进行处理 return supplier.get(request).next() .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { // 调用 getInstanceResponse 获取选择的实例 Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); } // ThreadLocalRandom.current(): 获取当前线程的随机数生成器 // instances.size(): 获取可用服务实例的数量 // nextInt(bound): 生成 [0, bound) 范围内的随机整数 // 生成一个介于0(包含)和instances.size()(不包含)之间的随机整数 int index = ThreadLocalRandom.current().nextInt(instances.size()); ServiceInstance instance = instances.get(index); return new DefaultResponse(instance); } }

Read more

AI Agent 面试八股文100问:大模型智能体高频考点全解析(附分类指南和简历模板)

AI Agent 面试八股文100问:大模型智能体高频考点全解析(附分类指南和简历模板)

AI Agent 面试八股文100问:大模型智能体高频考点全解析(附分类指南和简历模板) 如果你对学成归来的简历没有概念,可以看看以下的模板先,毕竟先看清眼前的路,比奔跑更重要: 最终的AI Agent简历模板,点我跳转! 适用人群:LLM Agent、RAG、AutoGPT、LangChain、Function Calling 等方向的求职者与开发者 随着大模型技术的飞速演进,AI Agent(智能体) 已成为工业界和学术界共同关注的焦点。无论是 AutoGPT、LangChain 还是 LlamaIndex,背后都离不开对 Agent 架构、推理机制、工具调用等核心能力的深入理解。 本文系统整理了 AI Agent 方向的 100 道高频面试问题,覆盖 基础概念、架构设计、推理决策、工具调用、记忆管理、评估方法、安全对齐、

By Ne0inhk
人工智能、机器学习和深度学习,其实不是一回事

人工智能、机器学习和深度学习,其实不是一回事

一、人工智能、机器学习与深度学习的真正区别 在当今科技领域,我们经常听到人工智能、机器学习和深度学习这三个词。它们虽然相关,但含义不同。 1.1 人工智能 人工智能是计算机科学的一个分支,旨在研究如何合成与分析能够像人一样行动的计算主体。简单来说,AI 的目标是利用计算机来模拟甚至替代人类大脑的功能。 一个理想的 AI 系统通常具备以下特征:像人一样思考、像人一样行动、理性地思考与行动。 1.2 机器学习 机器学习是实现人工智能的一种途径。它的核心定义是:赋予计算机在没有被显式编程的情况下进行学习的能力。 与传统的基于规则的编程不同,机器学习不依赖程序员手写每一条逻辑指令,而是通过算法让机器从大量数据中寻找规律,从而对新的数据产生预测或判断。 1.3 深度学习 深度学习是机器学习的一种特殊方法,也称为深度神经网络。它受人类大脑结构的启发,通过设计多层的神经元网络结构,来模拟万事万物的特征表示。 1.4 三者之间的层级关系 厘清这三者的关系对于初学者至关重要。人工智能 AI是最宏大的概念,包含了所有让机器变聪明的技术。机器学习 ML是 AI

By Ne0inhk
AI 基建:我拿到邀请码了,evomap 初体验,说说感受!——最后面有我本地节点上传的执行全指引。

AI 基建:我拿到邀请码了,evomap 初体验,说说感受!——最后面有我本地节点上传的执行全指引。

task: 我拿到邀请码了,evomap 初体验,说说感受!——最后面有我本地节点上传的执行全指引。 一天在 evomap 赚了几千积分,详细看我后续文章! 背景 昨天吃了口热饭,抢到了邀请码。并且我本地已经构建了evomap 的全套,今天就想简单看看具体有哪些功能! 航海日志-实操 “一个 Agent 学会,百万 Agent 继承。”从字面意思理解,应该有以下几种方向: 1、我的 agent,可以流动给别人。 2、我的 agent,可以自己迭代。 3、我的 agent,可以被另外一个 agent 学习。 4、我的 agent,可以从 A 环境无损迁移到 B 环境。 ...... 按照首页的指引,我应该是完成了接入我本地。 接入你的

By Ne0inhk
被问爆的Agent实战:从0到1搭建可落地AI智能体

被问爆的Agent实战:从0到1搭建可落地AI智能体

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 文章目录: * 【前言】 * 一、先搞懂:2026年爆火的AI Agent,到底是什么? * 1.1 Agent的核心定义 * 1.2 Agent的4大核心能力 * 1.3 2026年Agent的3个热门落地场景 * 二、框架选型:2026年6大主流Agent框架,新手该怎么选? * 三、实战环节:从0到1搭建可落地的“邮件处理Agent”(全程代码+步骤) * 3.1 实战准备:环境搭建(10分钟搞定) * 3.1.1 安装Python环境 * 3.1.2 创建虚拟环境(避免依赖冲突) * 3.

By Ne0inhk