OpenFeign远程调用组件的使用教程

使用的springcolud版本是

    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

1.OpenFeign介绍

  1. OpenFeign是个声明式WebService客户端,使用OpenFeign让编写WebService客户端更简单
  2. 它的使用方法是定义一个服务接口然后在上面添加注解
  3. OpenFeign也支持可拔插式的编码器和解码器
  4. SpringCloud对OpenFeign进行了封装使其支持了SpringMVC标准注解和HttpMessageConverters
  5. OpenFeign可以与Eureka和Ribbon组合使用以支持负载均衡

Feign

  • Feign是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端
  • Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务
  • Feign的使用方式是:使用Feign的注解定义接口,调用服务注册中心的服务
  • Feign支持的注解和用法请参考官方文档
  • Feign本身不支持SpringMVC的注解,它有一套自己的注解
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

Open Feign

  • OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。
  • OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口
  • OpenFeign通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

2.OpenFeign应用实例

使用OpenFeign远程调用服务提供方

  1. 导入依赖

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
  2. 在主程序使用注解"@EnableFeignClients"开启OpenFeign客户端功能

    package anyi.space.memberConsumer;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    /**
     * @ProjectName: distributedSystemLearn
     * @FileName: MemberConsumerApplication
     * @Author: 杨逸
     * @Data:2025/4/13 20:05
     * @Description:
     */
    //开启Feign客户端
    @EnableFeignClients
    @EnableDiscoveryClient
    @EnableEurekaClient
    @SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
    public class MemberConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(MemberConsumerApplication.class,args);
        }
    }
    
    
  3. 根据远程服务API定义Feign接口类

    • 使用注解"@FeignClient"标注这是一个OpenFeign客户端,注解参数value是注册到服务注册中心的服务提供方的名称
    • 根据远程API定义接口中的方法,然后使用SpringMC中的注解标注远程调用API的地址和需要传输的参数,接口方法的返回类型与接口API返回的类型保持一致,OpenFeign将通过动态代理的方式实现这个接口的方法,并封装返回的对象
    package anyi.space.memberConsumer.service;
    
    import anyi.sapce.common.entity.Member;
    import anyi.sapce.common.entity.ResponseResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestParam;
    
    /**
     * @ProjectName: distributedSystemLearn
     * @FileName: MemberService
     * @Author: 杨逸
     * @Data:2025/4/13 20:10
     * @Description:
     */
    //注入到IOC容器中
    @Component
    //声明这是一个Feign客户端,value的值为服务提供方注册到服务注册中心的名称
    @FeignClient(value = "member-service-provider")
    public interface MemberService {
        /**
         * 根据id查询会员信息
         * @param id
         * @return 会员信息
         * 使用SpringMVC的注解来声明远程调用接口和接口的参数
         */
        @GetMapping("/member-provider/member")
        ResponseResult<Member> getById(@RequestParam("id") Long id);
    
        /**
         * 新增会员信息
         * @param member
         * @return 成功ture,失败false
         */
        @PostMapping("/member-provider/member")
        ResponseResult<Boolean> save(@RequestBody Member member);
    }
    
    
  4. 在服务消费方Controller使用Feign接口进行远程调用

    package anyi.space.memberConsumer.controller;
    
    import anyi.sapce.common.entity.Member;
    import anyi.sapce.common.entity.ResponseResult;
    import anyi.space.memberConsumer.service.MemberService;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.util.DigestUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    
    /**
     * @ProjectName: distributedSystemLearn
     * @FileName: MemberConsumerController
     * @Author: 杨逸
     * @Data:2025/4/13 20:07
     * @Description:
     */
    @Slf4j
    @RequiredArgsConstructor
    @RestController
    @RequestMapping("/member")
    public class MemberConsumerController {
        private final MemberService memberService;
        private final DiscoveryClient discoveryClient;
        private final ObjectMapper objectMapper;
        @GetMapping
        public ResponseResult<Member> getMemberById(Long id){
            return memberService.getById(id);
        }
        @PostMapping
        public ResponseResult addMember(Member member){
            String hex = DigestUtils.md5DigestAsHex(member.getPwd().getBytes(StandardCharsets.UTF_8));
            member.setPwd(hex);
            return  memberService.save(member);
        }
    }
    
    
  5. 验证

    调用接口验证,经两次调用,验证成功访问到服务提供方,并且是轮询的负载均衡算法

3.OpenFeign设置日志级别

  1. 在配置类中注入Openfeign日志配置bean

    注入一个feign.Logger配置类

    package anyi.space.memberConsumer.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @ProjectName: distributedSystemLearn
     * @FileName: OpenFeignConfig
     * @Author: 杨逸
     * @Data:2025/5/16 20:23
     * @Description: 设置OpenFeign的日志级别
     */
    @Configuration
    public class OpenFeignConfig {
        @Bean
        public Logger.Level loggerLevel(){
            return Logger.Level.FULL;
        }
    }
    
    
  2. 在application.yaml配置文件中设置OpenFeign接口的日志级别

    logging:
      level:
        #配置Feign客户端的日志级别
        anyi.space.memberConsumer.service.MemberService: trace
    
    • 日志级别的分类
    日志级别说明
    error错误日志,指比较严重的错误,对正常业务有影响,需要运维配置监控的
    warn警告日志,一般的错误,对业务影响不大,但是需要开发关注
    info信息日志,记录排查问题的关键信息,如调用时间、出参入参等等
    debug用于开发DEBUG的,关键逻辑里面的运行时数据
    trace最详细的信息,一般这些信息只记录到日志文件中
  3. 验证配置的日志级别

4.OpenFeign超时设置

OpenFeign远程调用接口默认的超时时间是一秒,正常的接口一般都能满足这个要求

但也存在耗时的接口或者网络阻塞等情况,我们需要对超时时间间隔进行调整

  • 在application.yaml中配置超时时间
feign:
  client:
    #config是一个map
    config:
      #服务提供方名称,可以针对不同的服务提供方进行隔离的配置
      member-service-provider:
        #日志级别
        loggerLevel: full
        #设置为默认的契约(还原成原生注解),使用OpenFeign的注解
#        contract: feign.Contract.Default
        #读取资源超时时间
        readTimeout: 6000
        #建立连接超时时间
        connectTimeout: 1000