Feign的工做原理

Feign的工做原理

  • 主程序入口添加了@EnableFeignClients注解开启对FeignClient扫描加载处理。根据Feign Client的开发规范,定义接口并加@FeignClientd注解。git

  • 当程序启动时,回进行包扫描,扫描全部@FeignClients的注解的类,而且讲这些信息注入Spring IOC容器中,当定义的的Feign接口中的方法呗调用时,经过JDK的代理方式,来生成具体的RequestTemplate.当生成代理时,Feign会为每一个接口方法建立一个RequestTemplate。当生成代理时,Feign会为每一个接口方法建立一个RequestTemplate对象,改对象封装可HTTP请求须要的所有信息,如请求参数名,请求方法等信息都是在这个过程当中肯定的。github

  • 而后RequestTemplate生成Request,而后把Request交给Client去处理,这里指的时Client能够时JDK原生的URLConnection,Apache的HttpClient,也能够时OKhttp,最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发器服务之间的调用。spring

Feign注解剖析

@FeignClient注解主要被@Target({ElementType.TYPE})修饰,表示该注解主要使用在接口上。它具有了以下的属性:编程

  • name:指定FeignClient的名称,若是使用了Ribbon,name就做为微服务的名称,用于服务发现。json

  • url:url通常用于调试,能够指定@FeignClient调用的地址。api

  • decode404: 当发生404错误时,若是该字段为true,会调用decoder进行解码,不然抛出FeignException.数组

  • configuration:Feign配置类,能够自定或者配置Feign的Encoder,Decoder,LogLevel,Contract。app

  • fallback:定义容错的处理类,当调用远程接口失败或者超时时,会调用对应的接口的容错逻辑,fallback指定的类必须实现@Feign标记的接口。负载均衡

  • fallbacjFactory:工厂类,用于生成fallback类实例,经过这个属性能够实现每一个接口通用的容错逻辑们介绍重复的代码。ide

  • path:定义当前FeignClient的统一前缀。

Feign开启GZIP压缩

Spring Cloud Feign支持对请求和响应的进行GZIP压缩,以提升通讯效率。 在yml文件须要以下的配置

feign:
  compression:
    request:
      enabled: true #开请求压缩
      mimeTypes: #媒体类型 text/xml,application/xml,application/json
      minRequestSize: 2048 #最小的请求大小
    response:
      enabled: true #开启响应的压缩
复制代码

须要注意的是,在采用了压缩以后,须要使用二级制的方式进行数据传递,全部返回值就须要使用 ResponseEntity<byte[]> 接收.

@FeignClient(name = "github-client",url = "https://api.github.com",configuration = HelloFeignServiceConfig.class)
public interface HelloFeignService {

    /*
    这个返回类型若是采用了压缩,那么就是二进制的方式,就须要使用ResponseEntity<byte[]>做为返回值
     */
    @RequestMapping(value = "/search/repositories",method = RequestMethod.GET)
    ResponseEntity<byte[]> searchRepositories(@RequestParam("q")String parameter);
}
复制代码

Feign Client开启日志

须要在注解类添加配置的类:

@FeignClient(name = "github-client",url = "https://api.github.com",configuration = HelloFeignServiceConfig.class)
复制代码

注解类的代码以下:

@Configuration
public class HelloFeignServiceConfig {

    /**
     *
     * Logger.Level 的具体级别以下:
         NONE:不记录任何信息
         BASIC:仅记录请求方法、URL以及响应状态码和执行时间
         HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
         FULL:记录全部请求与响应的明细,包括头信息、请求体、元数据
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

}
复制代码

Feign的超时设置

Feign的调用分两层,即Ribbon层的调用和Hystrix的调用,高版本的Hystrix默认是关闭的。

  • 若是出现上述的错误,那么就须要添加以下的配置

    #请求处理的超时时间 ribbon.ReadTimeout: 12000 #请求连接超时时间 ribbon.ConnectionTimeout: 30000

  • 若是开启了Hystrix,Hystrix的超时报错信息以下:

此时能够添加以下配置:

hystrix:
  command:
    default:
      circuitBreaker:
        sleepWindowInMilliseconds: 30000
        requestVolumeThreshold: 50
      execution:
        timeout:
          enabled: true
        isolation:
          strategy: SEMAPHORE
          semaphore:
            maxConcurrentRequests: 50
          thread:
            timeoutInMilliseconds: 100000
复制代码

Feign的Post和Get的多参数传递

在SpringMVC中可使用Post或者Get请求轻易的请求到对应的接口而且讲参数绑定到POJO,可是Feign并无所有实现SpringMVC的功能,若是使用GET请求到接口,就没法将参数绑定到POJO,可是可使用如下的几种方式实现相应的功能。

  • 将POJO内的字段做为一个个的字段卸载接口上

  • 将参数编程一个map

  • POJO使用@RequestBody修饰,可是这个违反Restful的原则

如下的代码将Feign的请求拦截下来,将参数进行处理以后统一的编程map

@Component
public class FeignRequestInterceptor implements RequestInterceptor{
    @Autowired
    private ObjectMapper objectMapper;



    @Override
    public void apply(RequestTemplate requestTemplate) {
        // feign 不支持 GET 方法传 POJO, json body转query
        if (requestTemplate.method().equalsIgnoreCase("GET") && requestTemplate.body()!=null){
            try {
                JsonNode jsonNode = objectMapper.readTree(requestTemplate.body());
                requestTemplate.body(null);
                Map<String,Collection<String>> queries = new HashMap<>();
                buildQuery(jsonNode,"",queries);
                requestTemplate.queries(queries);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
    private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
        if (!jsonNode.isContainerNode()) {   // 叶子节点
            if (jsonNode.isNull()) {
                return;
            }
            Collection<String> values = queries.get(path);
            if (null == values) {
                values = new ArrayList<>();
                queries.put(path, values);
            }
            values.add(jsonNode.asText());
            return;
        }
        if (jsonNode.isArray()) {   // 数组节点
            Iterator<JsonNode> it = jsonNode.elements();
            while (it.hasNext()) {
                buildQuery(it.next(), path, queries);
            }
        } else {
            Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> entry = it.next();
                if (StringUtils.hasText(path)) {
                    buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
                } else {  // 根节点
                    buildQuery(entry.getValue(), entry.getKey(), queries);
                }
            }
        }
    }

}
复制代码

除了上面的方法以外还可使用venus-cloud-feign这个依赖,该依赖实现了GET请求的参数Map包装,无需再去本身写。依赖以下:

<!-- https://mvnrepository.com/artifact/cn.springcloud.feign/venus-cloud-feign-core -->
<dependency>
    <groupId>cn.springcloud.feign</groupId>
    <artifactId>venus-cloud-feign-core</artifactId>
    <version>1.0.0</version>
</dependency>
复制代码

github的地址以下:github.com/SpringCloud…

相关文章
相关标签/搜索