Spring Cloud OpenFeign实现动态服务名调用的示例代码

 更新时间:2025年06月06日 10:24:38   作者:要阿尔卑斯吗  
在微服务架构中,我们经常需要根据动态传入的服务名来远程调用其他服务,例如,你的业务中可能有多个子服务:service-1、service-2……需要动态决定调用哪个,所以本文给大家介绍了Spring Cloud OpenFeign 实现动态服务名调用指南,需要的朋友可以参考下

场景背景

在微服务架构中,我们经常需要根据动态传入的服务名来远程调用其他服务。例如,你的业务中可能有多个子服务:service-1service-2……需要动态决定调用哪个。

通常我们使用如下方式注入 Feign 客户端:

 @FeignClient(name = "service")
 public interface FeignClient {
     @PostMapping("/api/push")
     void pushMessage(@RequestBody PushMessageRequest request);
 }

但这种写法服务名是静态写死的,不能根据运行时的参数进行动态选择。

错误用法:FeignClientFactory

很多开发者会尝试用 Spring 内部的 FeignClientFactory

 @Resource
 private FeignClientFactory feignClientFactory;
 ​
 FeignClient FeignClient = feignClientFactory.getInstance(serviceName, FeignClient.class);

这种方式只能获取 @FeignClient(name="xxx") 注册的静态实例,而不能真正实现动态服务调用。

  • 适用场景:获取已经 @FeignClient 声明过的 bean。
  • 不适用:动态服务名(如从数据库或配置中传入)+ 动态构建 Feign 实例。

正确方式:自定义动态 Feign 客户端工厂

要想实现真正的动态服务名 + 负载均衡 + 支持配置和拦截器的 Feign 客户端,我们需要手动构造并注入 Feign 客户端

核心思路:

  • 使用 Spring Cloud 提供的 Feign.Builder(必须是 Spring 注入的)
  • 配合 LoadBalancerClient 实现服务发现与负载均衡
  • 手动构建 Feign 接口实例

一、配置 Feign.Builder

 @Configuration
 public class FeignBuilderConfig {
 ​
     @Bean
     @Scope("prototype")
     public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
         return Feign.builder()
                 .contract(new SpringMvcContract())
                 .encoder(new SpringEncoder(messageConverters))
                 .decoder(new SpringDecoder(messageConverters))
                 .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1), 3))
                 .options(new Request.Options(3000, 5000))
                 .logger(new Logger.ErrorLogger())
                 .logLevel(Logger.Level.BASIC);
     }
 }

二、自定义动态客户端工厂

 @Component
 @Slf4j
 public class DynamicFeignClientFactory {
 ​
     private final Feign.Builder feignBuilder;
     private final LoadBalancerClient loadBalancerClient;
 ​
     public DynamicFeignClientFactory(Feign.Builder feignBuilder,
                                      LoadBalancerClient loadBalancerClient) {
         this.feignBuilder = feignBuilder;
         this.loadBalancerClient = loadBalancerClient;
     }
 ​
     public <T> T getClient(String serviceName, Class<T> clazz) {
         int maxRetry = 3;
         int retryCount = 0;
         Exception lastException = null;
 ​
         while (retryCount < maxRetry) {
             try {
                 ServiceInstance instance = loadBalancerClient.choose(serviceName);
                 if (instance == null) {
                     throw new RuntimeException("未找到可用的服务实例:" + serviceName);
                 }
 ​
                 String url = instance.getUri().toString();
                 log.info("选择的 Feign 客户端目标地址为:{}", url);
                 return feignBuilder.target(clazz, url);
 ​
             } catch (Exception e) {
                 lastException = e;
                 log.warn("第 {} 次尝试获取 Feign 客户端失败,服务名:{},错误信息:{}", retryCount + 1, serviceName, e.getMessage());
                 retryCount++;
                 try {
                     Thread.sleep(500L);
                 } catch (InterruptedException ignored) {}
             }
         }
 ​
         throw new RuntimeException("创建 Feign 客户端失败,服务名:" + serviceName, lastException);
     }
 }

三、使用方式

原始写法(错误):

 @Resource
 private FeignClientFactory feignClientFactory;
 ​
 FeignClient FeignClient = feignClientFactory.getInstance(serviceName, FeignClient.class); 

正确写法:

@Resource
private DynamicFeignClientFactory feignClientFactory;

FeignClient FeignClient = feignClientFactory.getClient(ServerName, FeignClient.class);
FeignClient.pushMessage(new PushMessageRequest(Ids, senderEventMessage));

补充说明

  • Spring 注入的 Feign.Builder 会自动继承全局配置(超时、日志、拦截器等)。
  • 支持服务名动态路由,自动走 Spring Cloud LoadBalancer。
  • 每次调用可绑定到不同的服务实例(支持轮询/自定义负载策略)。
  • 避免直接 new Feign.Builder(),否则会失去 Spring 集成能力。

1. DynamicFeignClientFactory 类

 @Component
 @Slf4j
 public class DynamicFeignClientFactory {
 ​
     private final Feign.Builder feignBuilder;
     private final LoadBalancerClient loadBalancerClient;
 ​
     public DynamicFeignClientFactory(Feign.Builder feignBuilder,
                                      LoadBalancerClient loadBalancerClient) {
         this.feignBuilder = feignBuilder;
         this.loadBalancerClient = loadBalancerClient;
     }
 ​
     public <T> T getClient(String serviceName, Class<T> clazz) {
         ...
     }
 }

功能说明:

这是 动态创建 Feign 客户端 的核心工厂类,解决了 Spring Cloud @FeignClient 无法支持运行时动态服务名的问题。

核心逻辑:

  • 使用 Spring 提供的 LoadBalancerClient 动态选择某个服务的实例(支持 Eureka/Nacos 等注册中心)。
  • 使用 Spring 注入的 Feign.Builder 构建 Feign 客户端实例,绑定目标实例地址
  • 加了简单的重试逻辑(最多3次),提升服务不稳定时的容错性。

为什么不能直接用 FeignClientFactory

  • FeignClientFactory#getInstance 是静态注册的,依赖启动时的 @FeignClient(name="xxx")不能做到动态服务名运行时创建实例
  • 而本类是自己构造目标地址,可通过服务名运行时切换服务。

2. FeignBuilderConfig 类

 @Configuration
 public class FeignBuilderConfig {
 ​
     @Bean
     @Scope("prototype")
     public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
         return Feign.builder()
                 .contract(new SpringMvcContract())
                 .encoder(new SpringEncoder(messageConverters))
                 .decoder(new SpringDecoder(messageConverters))
                 .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1), 3))
                 .options(new Request.Options(3000, 5000))
                 .logger(new Logger.ErrorLogger())
                 .logLevel(Logger.Level.BASIC);
     }
 }

功能说明:

这是自定义的 Feign 构造器配置,确保动态创建的 Feign 实例拥有 Spring 的 HTTP 编解码器、契约协议、超时、重试等设置

关键配置解读:

配置项作用说明
SpringMvcContract让 Feign 支持 @RequestMapping、@GetMapping 等 Spring MVC 风格注解
SpringEncoder/Decoder使用 Spring Boot 的 HttpMessageConverter 做 JSON 编解码(默认支持 Jackson、Gson 等)
Retryer.Default(...)设置重试机制:初始延迟100ms,最大延迟1s,最多重试3次
Request.Options(...)设置连接超时为3秒,请求响应超时为5秒
Logger.ErrorLogger + BASIC开启日志,仅记录错误请求的基本信息(节省性能)
@Scope("prototype")每次注入都创建一个新的 Feign.Builder(防止多实例干扰)

为什么不能直接用 Feign.builder()

如果你直接用 Feign.builder()

  • 不具备 Spring 编解码器能力;
  • 没有 Spring 的日志、重试、超时等配置支持;
  • 无法识别 @RequestMapping 等注解;
  • 无法使用负载均衡(因为没注入 LoadBalancerClient);

你必须用 Spring 注入的 Feign.Builder,并设置好契约与编解码器,才能让它具备 @FeignClient 的能力。

总结

配置类作用是否必须
DynamicFeignClientFactory实现动态服务名绑定并构建 Feign 客户端
FeignBuilderConfig注入支持 Spring 编解码、契约协议、重试、超时等功能的构造器

这两个配置类结合起来,实现了 “动态服务发现 + 动态客户端构建 + Spring 完整能力支持” ,是 Spring Cloud Feign 动态服务名调用的标准做法之一。

以上就是Spring Cloud OpenFeign实现动态服务名调用的示例代码的详细内容,更多关于Spring Cloud OpenFeign服务名调用的资料请关注脚本之家其它相关文章!

相关文章

  • 轻松掌握java外观模式

    轻松掌握java外观模式

    这篇文章主要帮助大家轻松掌握java外观模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • JAVA实现二维码生成加背景图代码实例

    JAVA实现二维码生成加背景图代码实例

    这篇文章主要介绍了JAVA实现二维码生成加背景图代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • SpringMVC Mock测试实现原理及实现过程详解

    SpringMVC Mock测试实现原理及实现过程详解

    这篇文章主要介绍了SpringMVC Mock测试实现原理及实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • 基于注解的springboot+mybatis的多数据源组件的实现代码

    基于注解的springboot+mybatis的多数据源组件的实现代码

    这篇文章主要介绍了基于注解的springboot+mybatis的多数据源组件的实现,会使用到多个数据源,文中通过代码讲解的非常详细,需要的朋友可以参考下
    2021-04-04
  • Mybatis执行多条语句/批量更新方式

    Mybatis执行多条语句/批量更新方式

    这篇文章主要介绍了Mybatis执行多条语句/批量更新方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Java测试题 实现一个注册功能过程解析

    Java测试题 实现一个注册功能过程解析

    这篇文章主要介绍了Java测试题 实现一个注册功能过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Java常用的八种排序算法与代码实现

    Java常用的八种排序算法与代码实现

    这篇文章主要给给大家分享Java常用的八种排序算法与代码实现,下面文章将详细介绍整个实现过程,感兴趣的小伙伙伴可以跟着小编一起来学习,希望对你有所帮助
    2021-10-10
  • java解析sina视频

    java解析sina视频

    本文介绍了一个java解析sina视频地址的例子,从这个例子中可以学习到java使用sax解析xml的方法,大家可以参考修改成其它功能
    2014-01-01
  • Spring之IOC底层原理详解

    Spring之IOC底层原理详解

    这篇文章主要介绍了Spring之IOC底层原理,内容详细,文章简单易懂,具有一定的参考价值,需要的朋友可以参考下
    2023-01-01
  • 一篇文章带你入门java面向对象

    一篇文章带你入门java面向对象

    这篇文章主要介绍了Java语言面向对象编程思想之类与对象实例详解,还是十分不错的,这里给大家分享下,需要的朋友可以参考,希望能帮到你
    2021-08-08

最新评论