摘要
本文介绍了OpenFeign在微服务中的作用、特点及用法,并通过代码展示如何基于SpringCloudAlibaba2021.0.5.0+OpenFeign3.1.8+Nacos2.2.0实现微服务之间的接口远程调用。
认识OpenFeign
OpenFeign是SpringCloud生态中的一个声明式HTTP客户端,用于微服务之间的通信。结合服务发现(如Eureka/Nacos)、负载均衡(Ribbon)和熔断机制(Hystrix)等功能,可实现微服务高效交互。
特点
- 与Eureka、Nacos等注册中心无缝集成,实现服务注册与发现。
- 通过接口和注解定义远程服务调用逻辑,替代传统的RestTemplate或HttpClient手动构造请求。
- 集成Ribbon或SpringCloud LoadBalancer,支持客户端负载均衡策略。
- 支持Hystrix,当服务不可用时自动触发降级逻辑。
- 支持Java对象与HTTP请求体/响应体的自动转换(如JSON/XML)。
- 可配置详细日志级别(DEBUG/INFO),便于调试和监控。
常用注解
- @FeignClient声明Feign客户端,指定目标服务名称value、路径url、Spring容器标识contextId、降级机制fallbackFactory等。
- @GetMapping、@PostMapping定义接口请求方式,value值必须是生产者接口的完整路径。
- @RequestParam、@RequestBody、@PathVariable、@RequestHeader定义接口参数。
注意
SpringCloudGateway缺少HttpMessageConverters Bean会导致Feign解码失败,需要手动定义HttpMessageConverters转换器。
代码示例
父模块pom.xml
xml
<modules>
<module>feign-consumer</module>
<module>feign-producer</module>
</modules>
<!--依赖版本管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
生产者模块feign-producer
1)引入依赖pom.xml
xml
<dependencies>
<!--nacos 注册中心 客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--网关依赖 路由转发+请求限流+身份认证+负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- SpringCloudLoadbalancer 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2)配置文件application.yml
yaml
server:
port: 12005 #服务端口
spring:
application:
name:feign-producer #服务名
servlet:
context-path: /
cloud:
nacos:
server-addr: localhost:8848 #nacos服务端地址,默认8848
discovery:
ephemeral:true #默认是临时实例
3)启动类
java
package org.coffeebeans;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* <li>ClassName: ProducerApplication </li>
* <li>Author: OakWang </li>
*/
@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(ProducerApplication.class);
springApplication.run(args);
log.info("ProducerApplication start success!");
}
}
4)请求接口
typescript
package org.coffeebeans.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* <li>ClassName: ProducerController </li>
* <li>Author: OakWang </li>
*/
@Slf4j
@RestController
@RequestMapping("/producer")
public class ProducerController {
/**
* 获取实时时间
*/
@GetMapping(value = "/api1")
public String api1() {
return "随机数:" + Math.random();
}
@PostMapping(value = "/api2")
public String api2(@RequestBody Object paramObject) {
return "对象:" + paramObject;
}
@GetMapping(value = "/api3")
public String api3(@RequestParam("param") String param) {
return "单参:" + param;
}
@GetMapping(value = "/api4/{id}")
public String api4(@PathVariable Long id) {
return "变量:" + id;
}
}
消费者模块feign-consumer
1)引入依赖pom.xml
xml
<dependencies>
<!--nacos 注册中心 客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloudLoadbalancer 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--网关依赖 路由转发+请求限流+身份认证+负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Jackson JSON 库(用于序列化/反序列化) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2)配置文件application.yml
yaml
server:
port:12006 #服务端口
spring:
application:
name: feign-consumer #作为消费者使用
servlet:
context-path: /
cloud:
nacos:
server-addr: localhost:8848 #nacos服务端地址,默认8848
discovery:
ephemeral: true #默认是临时实例
loadbalancer:
ribbon:
enabled:true #启动SpringCloudLoadbalancer
# 超时配置
feign:
client:
config:
default:
connectTimeout:5000
readTimeout:5000
# Feign日志级别
logging:
level:
feign.client: DEBUG
3)启动类
java
package org.coffeebeans;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* <li>ClassName: ConsumerApplication </li>
* <li>Author: OakWang </li>
*/
@Slf4j
@EnableFeignClients// 指定 Feign 接口包路径
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(ConsumerApplication.class);
springApplication.run(args);
log.info("ConsumerApplication start success!");
}
}
4)openfeign接口定义
less
package org.coffeebeans.service;
import org.coffeebeans.fallback.ProducerClientFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
//@FeignClient声明Feign客户端,指定目标服务名称value、路径url、Spring容器标识contextId、降级机制fallbackFactory
@FeignClient(contextId = "myFeignClient", value = "feign-producer", url = "http://localhost:12005", fallbackFactory = ProducerClientFallbackFactory.class)
public interface MyFeignClient {
@GetMapping(value = "/producer/api1")
String api1();
@PostMapping(value = "/producer/api2")
String api2(@RequestBody Object paramObject);
@GetMapping(value = "/producer/api3")
String api3(@RequestParam("param") String param);
@GetMapping(value = "/producer/api4/{id}")
String api4(@PathVariable("id") Long id);
}
5)降级处理
typescript
package org.coffeebeans.fallback;
import lombok.extern.slf4j.Slf4j;
import org.coffeebeans.service.MyFeignClient;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* <li>ClassName: ProducerClientFallbackFactory </li>
* <li>Author: OakWang </li>
* 降级实现类
*/
@Slf4j
@Component
public class ProducerClientFallbackFactory implements FallbackFactory<MyFeignClient> {
@Override
public MyFeignClient create(Throwable cause) {
return new MyFeignClient() {
@Override
public String api1() {
log.error("feign-consumer 调用 feign-producer 服务失败: {}", cause.getMessage());
return null;
}
@Override
public String api2(Object paramObject) {
log.error("feign-consumer 调用 feign-producer 服务失败: {}", cause.getMessage());
return null;
}
@Override
public String api3(String param) {
log.error("feign-consumer 调用 feign-producer 服务失败: {}", cause.getMessage());
return null;
}
@Override
public String api4(Long id) {
log.error("feign-consumer 调用 feign-producer 服务失败: {}", cause.getMessage());
return null;
}
};
}
}
6)接口调用
kotlin
package org.coffeebeans.controller;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.coffeebeans.service.MyFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <li>ClassName: ConsumerController </li>
* <li>Author: OakWang </li>
*/
@Slf4j
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private MyFeignClient myFeignClient; //引入openfeign接口
@GetMapping(value = "/test1")
public String test1() {
return "消费者->生产者:" + myFeignClient.api1();
}
@GetMapping(value = "/test2")
public String test2() {
ParamObject paramObject = new ParamObject();
paramObject.setParam("我是对象参数");
return"消费者->生产者:" + myFeignClient.api2(paramObject);
}
@Data
static class ParamObject {
private String param;
}
@GetMapping(value = "/test3")
public String test3() {
return "消费者->生产者:" + myFeignClient.api3("单参");
}
@GetMapping(value = "/test4")
public String test4() {
return "消费者->生产者:" + myFeignClient.api4(2L);
}
}
7)报错处理

arduino
原因:
Stringget ProducerTime4();返回使用text/plain,需自定义转换器。
Feign默认使用Spring Decoder进行响应解码,而Spring Decoder依赖HttpMessageConverters。
SpringCloud Gateway基于WebFlux,缺少HttpMessageConverters Bean导致Feign解码失败,抛出No qualifying bean of type 'HttpMessageConverters'错误。
解决:
自定义转换器 FeignConfig和HttpMsgConverConfig
7.1)消费者 自定义转换器FeignConfig
arduino
package org.coffeebeans.config;
import feign.codec.Decoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
/**
* <li>ClassName: FeignConfig </li>
* <li>Author: OakWang </li>
*/
@Configuration
public class FeignConfig {
/**
* 配置Feign客户端的Decoder bean
* 用于解码响应数据为ResponseEntity对象
*/
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
/**
* 配置Feign使用的HTTP消息转换器工厂
* 返回一个包含自定义Jackson消息转换器的HttpMessageConverters对象
*/
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter());
return () -> httpMessageConverters;
}
/**
* 自定义Jackson消息转换器
* 设置支持的媒体类型(text/html和application/json),并指定字符集为UTF-8
*/
public static class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
GateWayMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
mediaTypes.add(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}
}
7.2)消费者 自定义转换器HttpMsgConverConfig
kotlin
package org.coffeebeans.config;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import reactor.core.publisher.Mono;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* <li>ClassName: HttpMsgConverConfig </li>
* <li>Author: OakWang </li>
*/
@Configuration
public class HttpMsgConverConfig {
/**
* 定义一个Bean,用于从请求中提取客户端的IP地址作为限流的Key。
* KeyResolver是Spring Cloud Gateway提供的接口,用于动态解析限流的Key。
*/
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());
}
/**
* 定义一个Bean,用于配置HTTP消息转换器。
* ConditionalOnMissingBean表示只有在容器中不存在同类型Bean时才创建。
* HttpMessageConverters是Spring Boot中用于管理HTTP消息转换器的工具类。
*/
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
}
测试结果
1)启动nacos

2)启动生产者和消费者
3)调用消费者接口
总结
以上我们了解了OpenFeign在微服务中扮演的角色和作用,并通过代码实现了openfeign在微服务中的基本通信。本文中的代码示例是将feign接口和消费者集成在一起做展示,也可单独起一个模块来集中处理转发逻辑,但需要生产者和消费者都引入此模块。
关注公众号:咖啡Beans
在这里,我们专注于软件技术的交流与成长,分享开发心得与笔记,涵盖编程、AI、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。