Spring Cloud LoadBalancer结合Feign客户端负载均衡配置问题
问题:Spring Boot + Spring Cloud LoadBalancer 实现Feign客户端负载均衡配置问题
环境
- Spring Boot v3.2.6
- Spring Cloud v2023.0.1
- 目标:结合Spring Cloud LoadBalancer实现Feign客户端负载均衡
已配置代码
Feign客户端
@FeignClient(value = "eisec2") public interface Eisec2Client { @GetMapping(path = "base1/path/{number}", consumes = MediaType.APPLICATION_XML_VALUE) String getCaller(@PathVariable("number") String cli); @RequestMapping(path = "status", method = RequestMethod.GET) CallDetails getStatus(); }
负载均衡配置类
@RequiredArgsConstructor @EnableFeignClients("com.frequentis.isec_server.client") @LoadBalancerClient(value = "eisec2", configuration = LoadBalancerConfiguration.class) public class FeignEisec2InstanceBalancingConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
LoadBalancerConfiguration
public class LoadBalancerConfiguration { @Bean // <-- 注释该Bean时程序"可用" public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context, DiscoveryClient discoveryClient) { return ServiceInstanceListSupplier.builder() .withHints() .withBlockingDiscoveryClient() .withBlockingHealthChecks(new RestTemplate()) .build(context); } @Bean ReactorLoadBalancer<ServiceInstance> rrLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); final RoundRobinLoadBalancer roundRobinLoadBalancer = new RoundRobinLoadBalancer( loadBalancerClientFactory.getLazyProvider( name, ServiceInstanceListSupplier.class), name); return roundRobinLoadBalancer; } }
application.properties配置
spring.application.name=balanced-server spring.cloud.loadbalancer.enabled=true spring.cloud.discovery.client.simple.instances.eisec2[1].uri=http://localhost:6090/eisec/1 spring.cloud.discovery.client.simple.instances.eisec2[0].uri=http://10.16.207.***:6090/eisec/1 spring.liquibase.enabled=false server.http2.enabled=false
遇到的问题
- 注释
ServiceInstanceListSupplier的@Bean时,程序仅调用配置中第一个服务实例,无法实现负载均衡; - 取消注释该Bean时,会出现「No servers available for service: eisec2」警告,日志输出重复;
- 尝试非阻塞式
ServiceInstanceListSupplier时,报错缺少ReactiveDiscoveryClientBean,但项目不想引入Reactor依赖。
已确认DiscoveryClient能正确读取服务实例配置,求正确配置实现Feign客户端负载均衡。
解决方案
1. 修复ServiceInstanceListSupplier配置
当前配置的核心问题在于健康检查逻辑和实例提供者的构建方式:
- 默认的
withBlockingHealthChecks会检查/actuator/health端点,如果目标服务未暴露该端点,实例会被标记为不可用,导致「No servers available」警告; - 链式调用未正确传入
DiscoveryClient实例,导致实例加载异常。
修改LoadBalancerConfiguration中的instanceSupplier方法:
方案1:移除健康检查(若不需要)
@Bean public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context, DiscoveryClient discoveryClient) { return ServiceInstanceListSupplier.builder() .withBlockingDiscoveryClient(discoveryClient) .build(context); }
方案2:自定义健康检查路径(若需要健康校验)
如果要保留健康检查,需指定目标服务的健康检查接口(比如你的/status接口):
@Bean public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context, DiscoveryClient discoveryClient) { RestTemplate restTemplate = new RestTemplate(); // 基于阻塞式DiscoveryClient构建实例提供者 DiscoveryClientServiceInstanceListSupplier baseSupplier = new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment()); // 绑定健康检查逻辑,指定你的服务健康检查路径 HealthCheckServiceInstanceListSupplier healthCheckSupplier = new HealthCheckServiceInstanceListSupplier(baseSupplier, restTemplate); healthCheckSupplier.setHealthCheckUrl("/status"); return healthCheckSupplier; }
2. 确保轮询负载均衡器生效
你的rrLoadBalancer Bean配置是正确的,只要ServiceInstanceListSupplier能正常返回可用实例,轮询策略就会自动生效。之前注释ServiceInstanceListSupplier时只调用第一个实例,是因为默认实例提供者未正确触发轮询逻辑。
3. 清理冗余配置(可选)
FeignEisec2InstanceBalancingConfiguration中定义的@LoadBalanced RestTemplate,若未被其他业务逻辑使用,可直接移除,避免对Feign客户端的负载均衡逻辑造成干扰。
4. 验证配置
修改完成后,启动项目调用Feign接口,观察日志是否轮询调用两个实例。可开启调试日志查看实例选择过程:
logging.level.org.springframework.cloud.loadbalancer=DEBUG
关键注意事项
- 确认目标服务实例的
uri配置正确,末尾不要添加多余路径(Feign接口路径会自动拼接在uri之后); - 若使用健康检查,必须保证目标服务的健康检查接口能返回200状态码,否则实例会被排除在可用列表之外。
内容的提问来源于stack exchange,提问作者greengold
相关产品推荐
相关产品推荐

