使用OpenFeign实现微服务间通信

Java教程 2025-09-18

摘要

本文介绍了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

<modules>
    <module>feign-consumermodule>
    <module>feign-producermodule>
modules>


<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>2021.0.8version>
            <type>pomtype>
            <scope>importscope>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-dependenciesartifactId>
            <version>2021.0.5.0version>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

生产者模块feign-producer

1)引入依赖pom.xml

<dependencies>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-loadbalancerartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
dependencies>

2)配置文件application.yml

server:
  port: 12005 #服务端口

spring:
  application:
    name:feign-producer   #服务名
  servlet:
    context-path: /
  cloud:
    nacos:
      server-addr: localhost:8848 #nacos服务端地址,默认8848
      discovery:
        ephemeral:true   #默认是临时实例

3)启动类

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;

/**
 * 
  • ClassName: ProducerApplication
  •  *
  • Author: OakWang
  •  */
    @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)请求接口

    package org.coffeebeans.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.*;
    import java.util.Date;
    
    /**
     * 
  • ClassName: ProducerController
  •  *
  • Author: OakWang
  •  */
    @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

     

    <dependencies>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-loadbalancerartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        
        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
    dependencies>
    

    2)配置文件application.yml

    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)启动类

    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;
    
    /**
     * 
  • ClassName: ConsumerApplication
  •  *
  • Author: OakWang
  •  */
    @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接口定义

    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)降级处理

    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;
    
    /**
     * 
  • ClassName: ProducerClientFallbackFactory
  •  *
  • Author: OakWang
  •  * 降级实现类  */
    @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)接口调用

    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;
    
    /**
     * 
  • ClassName: ConsumerController
  •  *
  • Author: OakWang
  •  */
    @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)报错处理

    图片

    原因:
    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
    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;
    
    /**
     * 
  • ClassName: FeignConfig
  •  *
  • Author: OakWang
  •  */
    @Configuration public class FeignConfig {     /**      * 配置Feign客户端的Decoder bean      * 用于解码响应数据为ResponseEntity对象      */     @Bean     public Decoder feignDecoder() {         return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));     }     /**      * 配置Feign使用的HTTP消息转换器工厂      * 返回一个包含自定义Jackson消息转换器的HttpMessageConverters对象      */     public ObjectFactory 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 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
    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;
    
    /**
     * 
  • ClassName: HttpMsgConverConfig
  •  *
  • Author: OakWang
  •  */
    @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> converters) {         return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));     } }

    测试结果

    1)启动nacos

    图片

    2)启动生产者和消费者

    图片图片图片3)调用消费者接口图片图片图片图片

    总结

    以上我们了解了OpenFeign在微服务中扮演的角色和作用,并通过代码实现了openfeign在微服务中的基本通信。本文中的代码示例是将feign接口和消费者集成在一起做展示,也可单独起一个模块来集中处理转发逻辑,但需要生产者和消费者都引入此模块。

    关注公众号:咖啡Beans

    在这里,我们专注于软件技术的交流与成长,分享开发心得与笔记,涵盖编程、AI、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。