当前位置: 首页 > web >正文

feign负载均衡

在没有网关和注册中心的情况下,如果你手动写死两个副本(服务实例)的地址,Feign 默认是不能自动完成负载均衡的,除非你 配合 Ribbon 或其他负载均衡组件 来实现。


✅ 实现方式(可行方案):

你可以通过以下方式让 Feign 实现负载均衡:

方式一:使用 Ribbon(Spring Cloud Netflix Ribbon)

虽然 Spring Cloud 已逐步废弃 Ribbon,但在一些项目中仍可用。

步骤:

  1. 定义服务名并配置多个地址:
my-service:ribbon:listOfServers: http://192.168.1.10:8080,http://192.168.1.11:8080
  1. Feign 接口中使用服务名:
@FeignClient(name = "my-service")
public interface MyServiceClient {@GetMapping("/api/test")String test();
}

这样,Feign + Ribbon 就可以在这两个地址之间自动负载均衡。


方式二:自定义 Feign 的 TargeterClient 实现自己的负载均衡逻辑(高级用法)

你可以不用 Ribbon,而是实现自己的 FeignClientFactoryBean 或注入一个自定义 Client 实例来控制请求分发逻辑,这样可以自己轮询或者按权重访问写死的服务实例。


❌ 不可行情况(默认行为):

如果你直接写死 Feign 的 URL:

@FeignClient(name = "my-service", url = "http://192.168.1.10:8080")

那么 Feign 就只能请求这个 URL,不会做负载均衡。


✅ 推荐实践(若不使用注册中心):

  1. 如果你不想接入注册中心(如 Eureka/Nacos):

    • 用 Ribbon(或 Spring Cloud LoadBalancer)+ Feign 是最简单的方式。
  2. 如果使用的是 Spring Cloud 2020+,建议用 Spring Cloud LoadBalancer 替代 Ribbon。


Spring Cloud LoadBalancer

Feign 搭配 Spring Cloud LoadBalancer(SC LoadBalancer)是 Spring Cloud 2020 及以后的推荐方式,用于在 没有 Ribbon、没有注册中心 的情况下,仍然实现 多实例负载均衡调用


✅ 一、场景目标

你有两个服务实例,地址写死,例如:

http://192.168.1.10:8080
http://192.168.1.11:8080

你希望 Feign 每次调用时在这两个地址之间负载均衡。


✅ 二、实现步骤

1. 添加依赖

确保使用的是 Spring Cloud 2020+,引入以下依赖:

<!-- Feign 依赖 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency><!-- LoadBalancer 依赖 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

2. 配置固定服务列表(写死多个地址)

application.yml 中配置:

spring:cloud:loadbalancer:clients:my-service:instances:- uri: http://192.168.1.10:8080- uri: http://192.168.1.11:8080

注意:这是 Spring Cloud LoadBalancer 的静态服务配置方式,不需要注册中心。


3. 定义 FeignClient 接口

@FeignClient(name = "my-service") // name 对应配置的 clients.my-service
public interface MyServiceClient {@GetMapping("/api/test")String test();
}

4. 启用 Feign 和 LoadBalancer

确保主类上有:

@EnableFeignClients
@SpringBootApplication
public class MyApp {public static void main(String[] args) {SpringApplication.run(MyApp.class, args);}
}

✅ 三、负载均衡策略(可选)

你可以自定义负载均衡策略:

@Configuration
public class LoadBalancerConfig {@Bean@LoadBalancedReactorLoadBalancer<ServiceInstance> myServiceLoadBalancer(Environment environment,LoadBalancerClientFactory factory) {return new RoundRobinLoadBalancer(factory.getLazyProvider("my-service", ServiceInstanceListSupplier.class),"my-service");}
}

默认就是 RoundRobin(轮询),也可以实现自定义策略。


✅ 四、调用示意

当你注入并调用 MyServiceClient.test() 方法时,Spring Cloud LoadBalancer 会自动从你配置的两个实例中轮询选择一个实例进行调用。


实现熔断

Spring Cloud LoadBalancer 本身不具备熔断功能。它的职责是实现客户端负载均衡(选择可用实例),不会探测实例是否健康,也不会在实例失败时自动熔断或剔除


✅ 你关心的问题:

一个副本在更新时不可用,LoadBalancer 会不会跳过它?

  • 不会自动跳过。
  • LoadBalancer 仍然会轮询或使用指定策略挑选这个“坏”实例。
  • 如果你不加控制,它会把请求发给这个更新中的实例,导致访问失败。

✅ 如何解决这个问题?

✅ 方案一:结合 Spring Cloud Circuit Breaker(推荐)

Spring Cloud 提供 spring-cloud-starter-circuitbreaker-resilience4j(或 Sentinel/Hystrix)来实现熔断机制。

步骤如下:

1. 添加依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
2. 修改你的 Feign 接口方法,结合熔断:
@FeignClient(name = "my-service")
public interface MyServiceClient {@GetMapping("/api/test")String test();
}

然后在调用方使用如下方式包装熔断:

@Service
public class MyServiceWrapper {private final MyServiceClient client;private final CircuitBreakerFactory<?, ?> cbFactory;public MyServiceWrapper(MyServiceClient client, CircuitBreakerFactory<?, ?> cbFactory) {this.client = client;this.cbFactory = cbFactory;}public String safeCall() {return cbFactory.create("myServiceCircuitBreaker").run(() -> client.test(), throwable -> fallback());}private String fallback() {return "fallback result";}
}

这样,如果某个实例失败,熔断器会在短期内阻止再请求那个实例。


✅ 方案二:借助健康检查 + 自定义过滤器

你也可以定期在服务端维护一个“健康实例列表”,通过 Spring Cloud LoadBalancer 的 ServiceInstanceListSupplier 自定义逻辑,把不健康的副本剔除(但这需要你自己维护健康状态)。


✅ 总结

功能是否由 LoadBalancer 提供
负载均衡✅ 是
熔断(失败跳过)❌ 否(需配合 Circuit Breaker)
实例健康剔除❌ 否(需你手动实现)

数据库获取副本

是的,Spring Cloud LoadBalancer 支持自定义服务实例列表来源,你完全可以把副本地址从数据库中读取,并动态更新这些地址。这样当某个副本不可用时,你只需从数据库中移除它,LoadBalancer 就不会再调用它。


✅ 实现目标

  • 使用 Spring Cloud LoadBalancer
  • 实例地址不来自注册中心,也不写死在配置文件中
  • 从数据库查询服务实例列表
  • 可以通过删除数据库记录控制负载均衡行为

✅ 实现方式

你需要自定义一个 ServiceInstanceListSupplier

1. 定义实体类(用于从数据库中取服务地址)

public class MyServiceInstance implements ServiceInstance {private final String serviceId;private final URI uri;public MyServiceInstance(String serviceId, String host, int port) {this.serviceId = serviceId;this.uri = URI.create("http://" + host + ":" + port);}@Override public String getInstanceId() { return null; }@Override public String getServiceId() { return serviceId; }@Override public String getHost() { return uri.getHost(); }@Override public int getPort() { return uri.getPort(); }@Override public boolean isSecure() { return false; }@Override public URI getUri() { return uri; }@Override public Map<String, String> getMetadata() { return Collections.emptyMap(); }
}

2. 自定义 ServiceInstanceListSupplier

@Component
public class DbServiceInstanceListSupplier implements ServiceInstanceListSupplier {private final String serviceId = "my-service";private final MyInstanceRepository repository; // 你自己的DAO类public DbServiceInstanceListSupplier(MyInstanceRepository repository) {this.repository = repository;}@Overridepublic String getServiceId() {return serviceId;}@Overridepublic Flux<List<ServiceInstance>> get() {List<MyInstanceEntity> dbInstances = repository.findAll(); // 从数据库获取List<ServiceInstance> instances = dbInstances.stream().map(i -> new MyServiceInstance(serviceId, i.getHost(), i.getPort())).collect(Collectors.toList());return Flux.just(instances);}
}

3. 启用 Feign 调用

@FeignClient(name = "my-service")
public interface MyServiceClient {@GetMapping("/api/test")String test();
}

✅ 实现效果

  • 每次 LoadBalancer 获取实例列表时,都会从数据库中查询;
  • 当你想“熔断”某个副本,只需从数据库中移除该条记录;
  • 系统会自动跳过失效副本。

⚠️ 注意

  • get() 方法可以加缓存(比如用 @Scheduled 定时刷新实例列表),避免每次都查库;
  • 确保数据库查询不会成为瓶颈。

自定义 Feign 的负载均衡逻辑

当然可以,下面我给你一个完整的示例:自定义 Feign 的 Client 实现负载均衡逻辑,不依赖 Ribbon、Spring Cloud LoadBalancer,也不需要注册中心。


✅ 场景

你手动指定两个服务实例地址:

http://192.168.1.10:8080
http://192.168.1.11:8080

你希望 Feign 每次调用时,在它们之间轮询负载均衡。


✅ 步骤详解

1. 定义一个轮询负载均衡器

@Component
public class MySimpleLoadBalancer {private final List<String> servers = List.of("http://192.168.1.10:8080","http://192.168.1.11:8080");private final AtomicInteger index = new AtomicInteger(0);public String chooseServer() {int i = Math.abs(index.getAndIncrement() % servers.size());return servers.get(i);}
}

2. 自定义 Feign Client 实现类

@Component
public class MyFeignClient implements Client {private final Client delegate = new Client.Default(null, null); // 使用默认 Feign 客户端private final MySimpleLoadBalancer loadBalancer;public MyFeignClient(MySimpleLoadBalancer loadBalancer) {this.loadBalancer = loadBalancer;}@Overridepublic Response execute(Request request, Request.Options options) throws IOException {String baseUrl = loadBalancer.chooseServer();// 重写原始请求的 URLString originalUrl = request.url(); // 例如 http://dummy/api/xxxURI originalUri = URI.create(originalUrl);URI newUri = URI.create(baseUrl + originalUri.getPath() + (originalUri.getQuery() != null ? "?" + originalUri.getQuery() : ""));Request newRequest = Request.create(request.httpMethod(),newUri.toString(),request.headers(),request.body(),request.charset(),request.requestTemplate());return delegate.execute(newRequest, options);}
}

3. 注入到 Feign 配置中

@Configuration
public class FeignCustomConfig {@Beanpublic Client feignClient(MySimpleLoadBalancer loadBalancer) {return new MyFeignClient(loadBalancer);}
}

4. 定义 Feign 接口(URL 随便写,反正会被你自定义 Client 改写)

@FeignClient(name = "dummy", url = "http://dummy", configuration = FeignCustomConfig.class)
public interface MyServiceClient {@GetMapping("/api/test")String test();
}

✅ 效果

  • 每次调用 MyServiceClient.test() 时,都会通过你自己的轮询逻辑在两个地址之间切换;
  • 你可以随时改写轮询逻辑为:加权、最少连接数、健康检查等;
  • 不依赖 Ribbon、LoadBalancer 或注册中心。

Ribbon、LoadBalancer的实现原理

非常准确的问题!

是的,从 原理上讲,无论是旧版的 Ribbon,还是新版的 Spring Cloud LoadBalancer,它们实现 Feign 的客户端负载均衡,核心就是:替换 Feign 默认的 Client 实现,注入它们自己的逻辑


✅ Feign 调用流程回顾

Feign 的调用大致流程是:

  1. 创建 Feign 接口代理;
  2. 封装请求为 Request
  3. 调用 Client.execute(Request, Options)
  4. 执行真正的 HTTP 请求。

✅ Ribbon 的实现原理(旧版)

在使用 Ribbon 时,Spring Cloud 会注入一个 LoadBalancerFeignClient

public class LoadBalancerFeignClient implements Client {private final Client delegate;private final SpringClientFactory clientFactory;@Overridepublic Response execute(Request request, Request.Options options) {// 从 Request 的 URL 里提取服务名// 用 Ribbon 的负载均衡策略选择实例// 替换 URL,再用 delegate(如 OkHttp)执行请求}
}

特点:

  • 从请求 URL 中解析服务名(如 my-service);
  • 用 Ribbon 的 ILoadBalancer 查找实例;
  • 替换 URL;
  • 交由底层 HTTP 客户端(如 OkHttpClient)执行。

✅ Spring Cloud LoadBalancer 的实现原理(新版)

Ribbon 被废弃后,Spring Cloud 使用了 LoadBalancerClientFactory 来创建实例列表,替代 Ribbon。

注入的也是一个 LoadBalancerFeignClient,只是实现不同:

public class LoadBalancerFeignClient implements Client {private final Client delegate;private final LoadBalancerClientFactory clientFactory;@Overridepublic Response execute(Request request, Request.Options options) {// 从 serviceId 获取 ServiceInstanceListSupplier// 负载均衡选择一个实例// 替换 request.url 为真实 IP+端口// 调用底层 HTTP Client 发请求}
}

✅ 总结

框架作用原理
Feign 默认直接用 URL 发请求(无负载均衡)
Feign + Ribbon使用 Ribbon 的负载均衡器 + 重写 Client 逻辑
Feign + LoadBalancer使用 Spring Cloud LoadBalancer + 重写 Client 逻辑
你自定义的实现一样地,重写 Client,插入自己的负载均衡策略

http://www.xdnf.cn/news/4521.html

相关文章:

  • linux(centos)联网情况下部署
  • 第一章——typec电路
  • SpirngAI框架 Advisor API详解
  • 【无标题】如何在sheel中运行Spark
  • 基于Django框架开发的企业级IT资产管理系统
  • Topic和Partition的关系是什么?为什么需要分区? (Topic是逻辑分类,Partition是物理分片;提升并行度和扩展性)
  • 【信息系统项目管理师-论文真题】2005下半年论文详解(包括解题思路和写作要点)
  • mint系统详解详细解释
  • 开源数学推理模型DeepSeek-Prover-V2:88.9%通过率+超长推理链
  • 数造科技携 DataBuilder 亮相安徽科交会,展现“DataOps +AI”双引擎魅力
  • 机器学习之嵌入(Embeddings):从理论到实践
  • LangChain第二讲:不设置环境变量也能调用LLM大模型吗?(更简单地调用LLM)
  • LabVIEW表面粗糙度测量及算法解析
  • Python cv2视频处理基础:从入门到实战
  • 我如何在ubuntu截图和屏幕录制,有什么好用的免费的软件吗?
  • C++ 基础复习
  • 蓝牙L2CAP协议概述
  • 微机控制电液伺服拉扭疲劳试验系统
  • 004 Linux基本指令
  • C语言| 递归求两个数的最大公约数
  • 17.Three.js 光照系统之《LightProbe》详解指南(含 Vue 3示例)
  • 准确--Notepad++ 实用的插件介绍
  • 【论文阅读】HunyuanVideo: A Systematic Framework For Large Video Generative Models
  • Linux系统安装指南
  • vue2中的组件注册
  • Landsat WRS介绍 及 Polygon定位WRS算法
  • WPF MVVM入门系列教程(六、ViewModel案例演示)
  • [Windows] 蓝山看图王 1.0.3.21021
  • JGL021垃圾渗滤液小试实验装置
  • 三、大模型原理:图文解析MoE原理