概述

OpenFeign是什么

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡

Feign能干什么

Feign旨在使编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。


Feign集成了Ribbon

利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

Feign和OpenFeign两者区别

 OpenFeign使用步骤

其实就是接口加注解——微服务调用接口+@FeignClient

POM依赖

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

yaml配置文件

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

在主启动类上添加上 @EnableFeignClients 注解开启Feign的服务

新建一个OpenFeign服务调用接口 添加上 @FeignClient 注解启用Feign

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entity.CommonResult;
import com.atguigu.springcloud.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * <p>
 * OpenFeign服务调用接口
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 14:34
 */
//一定记得要添加@Component注解将该接口注册为spring组件
@Component
//该注解就是使用Feign服务,里面的值就是要调用的微服务在注册中心的名称
@FeignClient("CLOUD-PAYMENT-SERVICE") 
public interface PaymentFeignService {
    //被调用服务端的方法体,Mapping里面的路径需要写全
    @PostMapping("/payment/create")
    CommonResult<Integer> create(@RequestBody Payment payment);

    @GetMapping("/payment/lb")
    String getPaymentLB();

    @GetMapping("/payment/get/{id}")
    CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

新建Controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entity.CommonResult;
import com.atguigu.springcloud.entity.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import com.netflix.discovery.converters.Auto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 *
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 14:43
 */
@Slf4j
@RestController
@RequestMapping("consumer")
public class OrderFeignController {
    //将OpenFeign服务调用接口注入进来
    @Autowired
    private PaymentFeignService paymentFeignService;

    @GetMapping("/lb")
    public String getPaymentLB() {
        //直接调用OpenFeign服务调用接口中的方法实现服务调用
        return paymentFeignService.getPaymentLB();
    }

    @GetMapping("get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        return paymentFeignService.getPaymentById(id);
    }
}

测试

 远程服务调用成功,可以看出Feign自带负载均衡配置项

小总结

 OpenFeign超时控制

在实际的运用中,当consumer调用服务时会发生等待时间过长的情况,但是我们是不能让consumer等待这么长时间的。因此必须采用超时控制。

那么就来模拟一下超时设置,故意设置超时演示出错情况(OpenFeign默认一秒钟超时)

服务提供方8001,8002故意写暂停程序

    @GetMapping("timeOut")
    public String paymentFeignTimeOut() {
        try {
            TimeUnit.SECONDS.sleep(3); //睡眠3秒钟再返回端口号
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return serverPort;
    }

然后相同的在80端口的consumer模块中远程调用provider模块里的该超时方法

测试

直接报错 

那么该怎么处理这种情况呢

默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。

yml文件中开启配置

#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

重启80服务再测试

 此次等待时间较长,但是服务器响应成功

OpenFeign日志打印功能

Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出

日志级别

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

配置日志bean

package com.atguigu.springcloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <p>
 * Feign配置类
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 15:54
 */
@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL; //设置日志级别为FULL
    }
}

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

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.atguigu.springcloud.service.PaymentFeignService: debug

启动服务访问,后台日志查看

2022-04-09 16:01:16.008 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] ---> GET http://CLOUD-PAYMENT-SERVICE/payment/get/1 HTTP/1.1
2022-04-09 16:01:16.008 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] ---> END HTTP (0-byte body)
2022-04-09 16:01:16.118  INFO 29608 --- [p-nio-80-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2022-04-09 16:01:16.136  INFO 29608 --- [p-nio-80-exec-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-CLOUD-PAYMENT-SERVICE
2022-04-09 16:01:16.136  INFO 29608 --- [p-nio-80-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: CLOUD-PAYMENT-SERVICE instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=CLOUD-PAYMENT-SERVICE,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2022-04-09 16:01:16.141  INFO 29608 --- [p-nio-80-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2022-04-09 16:01:16.153  INFO 29608 --- [p-nio-80-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2022-04-09 16:01:16.154  INFO 29608 --- [p-nio-80-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client CLOUD-PAYMENT-SERVICE initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=CLOUD-PAYMENT-SERVICE,current list of Servers=[10.8.0.106:8002, 10.8.0.106:8001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:2;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:10.8.0.106:8002;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
, [Server:10.8.0.106:8001;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7e8e81f6
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] <--- HTTP/1.1 200 (192ms)
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] connection: keep-alive
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] content-type: application/json
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] date: Sat, 09 Apr 2022 08:01:16 GMT
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] keep-alive: timeout=60
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] transfer-encoding: chunked
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] 
2022-04-09 16:01:16.203 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] {"code":200,"message":"查询成功,serverPort:8002","data":{"id":1,"serial":"张三"}}
2022-04-09 16:01:16.203 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] <--- END HTTP (87-byte body)
2022-04-09 16:01:17.149  INFO 29608 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐