微服务调用组件-Open Feign

Feign和OpenFeign

Feign是Netflix开发的声明式、模板化的HTTP客户端,可以方便快捷的调用Http请求;Spring Cloud OpenFeign对Feign进行了增强,支持了MVC的注解,通过动态代理的方式产生实现类,还整合了ribbon和nacos,实现了负载均衡。

Java中传统的调用远程接口的方式有HttpClient、Okhttp、HttpURLConnection、RestTemplate、WebClient,而OpenFeign使用HTTP请求远程服务时,就像调用本地接口一样方便,无需关注远程的交互细节。

Spring Cloud Alibaba整合OpenFeign

1. 快速使用

1.1 调用端依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.2 调用端启动注解

调用端启动类添加@EnableFeignClients注解

1.3 调用端编写调用接口

如果远程端的接口如下:

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/a/{id}")
    public String a(@PathVariable("id") Integer id) {
        return "Hello";
    }
}

则本地端创建类似远程端的本地接口:

package com.xc.order.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author wyp
 */
@FeignClient(value = "A-SERVICE",path = "/test")
public interface AFeignService {
    @RequestMapping("/a/{id}")
    //@PathVariable("id")里的值不能省略
    String a(@PathVariable("id") Integer id);
}

其中@FeignClient注解的value属性代表调用的远程服务名,path属性为由所有方法级映射使用的路径前缀。

1.4 调用该接口

调用AFeignService类中的接口可实现远程调用。

@Autowired
private AFeignService aFeignService;

@RequestMapping("/abc")
public String abc() {
    return aFeignService.a(1);
}
1.5 注意

当存在多个类的@FeignClient注解指定的value值相同时,启动会报“已经定义了具有该名称的 bean”错误,The bean '服务名.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.

解决方式:配置文件中加入spring.main.allow-bean-definition-overriding=true允许 bean 定义覆盖。

消费者和提供者需在同一个命名空间内,否则会出现com.netflix.client.ClientException: Load balancer does not have available server for client:XX错误。

2. OpenFeign日志级别配置

2.1 定义指定日志级别的配置类(全局配置)
package com.xc.a.config;

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

/**
 * @author wyp
 */
@Configuration
public class FeignConfig {

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

其中Level枚举类有四种值:

  • NONE: 没有记录。
  • BASIC: 仅记录请求方法和 URL 以及响应状态代码和执行时间。
  • HEADERS: 记录基本信息以及请求和响应标头。
  • FULL: 记录请求和响应的标头、正文和元数据。
2.2 局部配置

当配置加了@Configuration注解之后,会作用于全局,所有的服务都会生效,当不加该注解后,可通过@FeignClient注解指定哪个服务,该注解configuration属性指定哪个配置类。

去掉上面配置类代码中的@Configuration注解,并在要调用的服务上做如下配置:

@FeignClient(value = "FEIGN-B",path = "/b",configuration = FeignConfig.class)
public interface FeignBService {
    @RequestMapping("/add")
    String add();
}

当然也可以通过配置文件的方式代替配置类,如下:

feign:
  client:
    config:
      # 服务名
      FEIGN-B:
        # 日志级别
        loggerLevel: FULL

这样,只会在调用服务名为FEIGN-B的时候输出日志。当然由于spring boot默认的日志级别为info,所以还需要将yml中做一下配置,将该服务类包的路径重新定义日志级别:

logging:
  level:
    com.xc.a.feign: debug

boot微服务调用 微服务调用组件_spring

下面内容的局部和全局配置的方式都可以按照这一小节来配置,就不多赘述了。

3. Feign契约配置

原生Feign不支持MVC的注解,当Feign升级为OpenFeign后,里面的注解将不可用,这时可做契约配置,就不需要替换里面所有的原生Feign注解了。

3.1 通过配置类的方式
@Bean
public Contract feignContract() {
	return new Contract.Default();
}
3.2 通过配置文件的方式
feign:
  client:
  	config:
      #对应微服务
      A-SERVICE: 
          loggerLevel: FULL
              # 指定Feign原生注解契约配置
              contract: feign.Contract.Default

4. OpenFeign超时时间

4.1 配置类的方式设置超时时间

Request.Options(long connectTimeout, TimeUnit connectTimeoutUnit, long readTimeout, TimeUnit readTimeoutUnit, boolean followRedirects)方法,connectTimeout为连接超时时间(默认为2秒),readTimeout为请求处理超时时间(即连接成功后响应时间,默认为5秒),TimeUnit 分别为其设置单位。

package com.xc.a.config;

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

import java.util.concurrent.TimeUnit;

/**
 * @author wyp
 */
@Configuration
public class FeignConfig {
    @Bean
    public Request.Options options() {
        return new Request.Options(5, TimeUnit.SECONDS,3000,TimeUnit.MILLISECONDS,true);
    }
}
4.2 配置文件的方式设置超时时间
feign:
  client:
    config:
      # 针对的服务名
      FEIGN-B:
        # 连接超时时间
        connectimeout: 5000
        # 请求处理超时时间
        readTimeout: 3000

5. OpenFeign自定义拦截器

客户端–①–>消费端–②–>提供端

springMVC的拦截器作用在①处,而feign的拦截器是作用在②处。

5.1 先自定义拦截器

通过实现RequestInterceptor类自定义拦截器

package com.xc.a.config;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.UUID;

/**
 * @author wyp
 */
public class MyFeignInterceptor implements RequestInterceptor {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    public void apply(RequestTemplate template) {
        String accessToken = UUID.randomUUID().toString();
        template.header("Authorization",accessToken);
        logger.info("feign拦截器--在这里做一些操作");
    }
}
5.2 配置类的方式

用Bean的方式注入MyFeignInterceptor自定义拦截器。

package com.xc.a.config;

import org.springframework.context.annotation.Bean;

/**
 * @author wyp
 */
public class FeignConfig {
    /**
     * 自定义拦截器
     */
    @Bean
    public MyFeignInterceptor myFeignInterceptor() {
        return new MyFeignInterceptor();
    }
}
5.3 配置文件的方式
feign:
  client:
    config:
      # 针对的服务名
      FEIGN-B:
        # 配置拦截器  自定义拦截器类路径
        requestInterceptors[0]:
          com.xc.a.config.MyFeignInterceptor