服务调用:OpenFeign详解

OpenFeign基本介绍

概述

服务之间如果需要相互访问,可以使用RestTemplate, 也可以使用 Feign客户端访问。它默认会使用Ribbon来实现负载均衡。

可以理解为Feign是一个超级方便的调用Spring Cloud远程服务的框架/工具,帮助开发者以更少耦合,更少代码,更快更兼容的方法进行远程服务调用。

什么是服务调用

顾名思义,就是服务之间的接口互相调用,在微服务架构中很多功能都需要调用多个服务才能完成某一项功能。

为什么要使用Feign

Feign 旨在使编写 JAVA HTTP 客户端变得更加简单,Feign 简化了RestTemplate代码,实现了Ribbon负载均衡,使代码变得更加简洁,也少了客户端调用的代码,使用 Feign 实现负载均衡是首选方案,只需要你创建一个接口,然后在上面添加注解即可。

Feign 是声明式服务调用组件,其核心就是:像调用本地方法一样调用远程方法,无感知远程 HTTP 请求。让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全无感知这是远程方法,无需关注与远程的交互细节,更无需关注分布式环境开发。

Feign vs OpenFeign

Feign内置了Ribbon,用来做客户端负载均衡调用服务注册中心的服务。
Feign 支持的注解和用法参考官方文档:https://github.com/OpenFeign/feign官方文档,使用 Feign 的注解定义接口,然后调用这个接口,就可以调用服务注册中心的服务。

Feign本身并不支持Spring MVC的注解,它有一套自己的注解,为了更方便的使用Spring Cloud孵化了 OpenFeign。并且支持了Spring MVC的注解,如@RequestMapping,@PathVariable等等。
OpenFeign的 @FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。

Feign的实现流程

  • 首先通过 @EnableFelgnClients注解开启FeignClient的功能 ,只有这个注解存在,才会在程序启动时开启对 @FeignClient注解的包扫描。
  • 根据Feign的规则实现接口,并在接口上加上@FeignClient注解。
  • 程序启动后,会进行包扫描,扫描所有的@FeignClient注解的类,并将这些信息注入到lOC容器中。
  • 当接口中的方法被调用时,通过JDK的动态代理生成具体的 RequestTemplate模板对象 。
  • 根据RequestTemplate再生成HTTP的Request对象。
  • Request对象交给Client处理,其中Client网络请求框架可以是 HttpURLConnection, HttpClient与OkHttp(主要看依赖是什么)。
  • 最后,Client被封装到LoadBalanceClient类,该类结合Ribbon做到了负载均衡。

OpenFeign使用步骤

1、服务消费端的POM文件添加依赖

<!-- spring cloud openfeign -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、yml配置文件

server:
  port: 80

spring:
  application:
    name: cloud-consumer-service

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

3、启动类添加@EnableFeignClients注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients // 开启对 @FeignClient注解的包扫描
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

4、新建RemoteUserService.java服务接口、使用@FeignClient注解

@Component
@FeignClient(value = "cloud-provider-service") // 服务提供者名称
public interface RemoteUserService {
    
    @RequestMapping("/user/search/v1") // 要调用的接口
    RestResult<List<UserTest>> searchUser(@RequestBody UserTest userTest);
    
}

5、消费者ConsumerController.java新增查询用户方法

@RestController
@Api(tags = "消费端")
public class ConsumerController {

    @Autowired
    private RemoteUserService remoteUserService;

    @ApiOperation(value = "查询用户")
    @PostMapping(value = "/search/v2")
    public RestResult<List<UserTest>> searchUser(@RequestBody UserTest userTest) {

        try {

            RestResult<List<UserTest>> listRestResult = remoteUserService.searchUser(userTest);

            return listRestResult;

        } catch (Exception e) {
            return RestResult.fail(ResultCode.DATA_ACCESS_ERROR);
        }

    }

}

6.测试

调用成功
在这里插入图片描述

OpenFeign超时控制

OpenFeign默认等待1秒钟,超过后报错

YML配置文件里设置OpenFeign客户端超时时间

feign:
  client:
    config:
      #这里填具体的服务名称(也可以填default,表示对所有服务生效)
      cloud-provider-service:
        #connectTimeout和readTimeout这两个得一起配置才会生效
        #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
        connectTimeout: 5000
        #指的是建立连接后从服务器读取到可用资源所用的时间
        readTimeout: 5000
      cloud-provider-service2: # 每个服务单独配置
        connectTimeout: 2000
        readTimeout: 2000

OpenFeign日志增强

Feign提供了日志打印功能,我们可以通过配置来调整日恙级别,从而了解Feign 中 Http请求的细节。

日志级别

  • NONE:默认的,不显示任何日志;
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

FeignConfig配置类

/**
 * FeignConfig配置类
 */
@Configuration
public class FeignConfig {

   /**
     * 创建重试器 (重试周期(50毫秒),最大重试周期(2000毫秒),最多尝试次数 6次 )
     * feign没有采用线性的重试机制而是采用的是一种指数级(乘法)的重试机制 每次重试时间  当前重试时间*= 1.5
     * @FeignClient(value = "Eureka-client", configuration = FeignConfig.class) 进行配置
     * @return
     */
    @Bean
    public Retryer getRetryer() {
        return new Retryer.Default(50, TimeUnit.SECONDS.toMillis(2), 6);
    }

    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

YML文件里需要开启日志的Feign客户端

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.da.springcloud.feignservice.RemoteUserService: debug

可以看到请求参数、返回结果等信息。
在这里插入图片描述

OpenFeign文件传输

OpenFeign本身不支持文件传输,需要在发送方进行一些配置。

添加feign支持提交from 表单的依赖

 <!-- feign支持提交from 表单 -->
 <dependency>
     <groupId>io.github.openfeign.form</groupId>
     <artifactId>feign-form</artifactId>
     <version>3.8.0</version>
 </dependency>
 <dependency>
     <groupId>io.github.openfeign.form</groupId>
     <artifactId>feign-form-spring</artifactId>
     <version>3.8.0</version>
 </dependency>

feign接口

@FeignClient(name = ServiceNameConstants.OFFICE_2_PDF_SERVICE, fallbackFactory = RemoteOffice2PdfFallbackFactory.class, configuration = RemoteOffice2PdfService.FormSupportConfig.class)
public interface RemoteOffice2PdfService {


    /**
     * office2pdf
     * @param file
     * @param pdfEditPermissionPwd
     * @return
     */
    @PostMapping(value = "/office2pdf/o2p/conver/v1", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    Response office2pdf(@RequestPart(value = "file") MultipartFile file, @RequestParam(value = "pdfEditPermissionPwd", required = false) String pdfEditPermissionPwd);


    class FormSupportConfig {
        @Autowired
        private ObjectFactory<HttpMessageConverters> messageConverters;
        // new一个form编码器,实现支持form表单提交
        @Bean
        public Encoder feignFormEncoder() {
            return new SpringFormEncoder(new SpringEncoder(messageConverters));
        }
    }
}

服务端

   /**
     * office转pdf
     * @param file office文件
     * @param pdfEditPermissionPwd pdf编辑权限密码
     * @return
     */
    @ApiOperation(value = "office转pdf", notes = "office转pdf,仅支持word、ppt、excel")
    @PostMapping(value = "/conver/v1", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "office文件,仅支持word、ppt、excel", required = true),
            @ApiImplicitParam(name = "pdfEditPermissionPwd", value = "pdf编辑权限密码,为空则不设置权限")
    })
    public void conver(@RequestPart MultipartFile file, @RequestParam(value = "pdfEditPermissionPwd", required = false) String pdfEditPermissionPwd, HttpServletResponse response) {
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值