Spring Cloud

Feign 概述

Feign 是一个声明web服务客户端,这便得编写web服务客户端更容易,使用Feign 创建一个接口并对它进行注解,它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,支持拦截器,支持日志,支持重试,相较于HTTPClient多了一种概念,面向接口。所有的请求服务都存放在对应的FeignClient接口中。类似于一种模板调用。省去了大量冗余在配置文件写的的URL配置项,而且直接以接口形式来展示远端的请求,配合合理的日志打印,可以直观的发现服务调用的过程,及参数。便于排查。


原生的netflix的feign
  • 先在pom文件中引入feign的依赖
<dependency>
	<groupId>com.netflix.feign</groupId>
	<artifactId>feign-core</artifactId>
	<version>8.18.0</version>
</dependency>
<dependency>
	<groupId>com.netflix.feign</groupId>
	<artifactId>feign-jackson</artifactId>
	<version>8.18.0</version>
</dependency>
<dependency>
	<groupId>com.netflix.feign</groupId>
	<artifactId>feign-okhttp</artifactId>
	<version>8.18.0</version>
</dependency>
  • feign-core是整个调用的核心
  • feign-jackson是用于加密解密,还有feign-Gson
  • feign-okhttp是一个处理网络请求的开源项目
okhttp相较于HttpClient
  1. 允许连接到同一个主机地址的所有请求,提高请求效率
  2. 共享Socket,减少对服务器的请求次数
  3. 通过连接池,减少了请求延迟
  4. 缓存响应数据来减少重复的网络请求
  5. 减少了对数据流量的消耗
  6. 自动处理GZip压缩

创建OkHttpClient,添加配置

@Bean
    public okhttp3.OkHttpClient okHttpClient(){
        okhttp3.OkHttpClient.Builder ClientBuilder = new okhttp3.OkHttpClient.Builder()
                .readTimeout(30, TimeUnit.SECONDS) //读取超时
                .connectTimeout(10, TimeUnit.SECONDS) //连接超时
                .writeTimeout(60, TimeUnit.SECONDS) //写入超时
                .connectionPool(new ConnectionPool(10 /*maxIdleConnections*/, 3, TimeUnit.MINUTES));
        return ClientBuilder.build();
    }
Feign构建自己的build

OkHttp 支持 SPDY (SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),并有更好的控制http请求

@Bean
    public XXX clientIdRemoteService() {
//        Feign.builder();


        log.info("初始化获取服务的Feign接口");
        return Feign.builder()
                .logger(new MyLogger())
                .logLevel(MyLogger.Level.FULL)
                .retryer(new MyRetryer(100L,1L,3))
//                .encoder(new GsonEncoder())
//                .decoder(new GsonDecoder())
                .decode404()
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .client(new OkHttpClient(okHttpClient))
                .target(XXX.class, server);
    }

可以配置自定义的日志和重试策略
logLevel用来指定日志的级别

logLevel
  • NONE:默认值,不进行日志记录
  • BASIC:记录请求方法、URL、响应状态代码和执行时间
  • HEADERS:除了 BASIC 记录的信息外,还包括请求头和响应头
  • FULL:记录全部日志,包括请求头、请求体、请求与响应的元数据
    server代表调用目标的主机
    配置的XXX.calss在调用build()方法是调用ReflectiveFeign去创建一个Feign代理后的对象。
Builder.java
Builder内部类中的build方法
 public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }

上述代码中的编码,解码支持热拔插。Sax支持以sax方式解析XMl,JAXB也是对XMl的编码器解码器

Feign的注解
1. @RequestLine(“GET /repos/{owner}/{repo}/contributors”)
 RequestLine注解声明请求方法(POST,GET,PUT… )和请求地址,可以允许有查询参数2. @Headers 设置静态请求头可以设置在全局也可以给某一个方法单独使用,注解中也支持参数
 @Headers(“Accept: application/json”), @Headers(“Content-Type: application/json”)
 @Headers(“X-Ping: {token}”)3. @Param(“owner”)
 所有的参数(包括RequestLine中的还有Headers中的)均采用@Param来来接收。4. @QueryMap可以用来传输动态参数。
 V find(@QueryMap Map<String, Object> queryMap);5. 动态传输请求头信息使用@HeaderMap @HeaderMap 注解设置的请求头优先于其他方式设置的也就说会覆盖@Headers()中设置的。
 void post(@HeaderMap Map<String, Object> headerMap);
 @Body("<login “user_name”="{user_name}" “password”="{password}"/>")
 void xml(@Param(“user_name”) String user, @Param(“password”) String password);
 // 这里JSON格式需要的花括号需要转码。
 @Body("%7B"search": “{search}”%7D")
 void json(@Param(“user_name”) String user, @Param(“password”) String password);
  1. @Body注解申明一个请求体模板,模板中可以带有参数,与方法中 @Param 注解申明的参数相匹配,使用@Body注解传输json对象是必须配合 @Headers(“Content-Type: application/json”)一起使用
    此处要说一个插件JAX-RS
    使用 JAX-RS 规范重写覆盖了默认的注解处理。
interface GitHub {
  @GET @Path("/repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}
// contract 方法配置注解处理器,注解处理器定义了哪些注解和值是可以作用于接口的
GitHub github = Feign.builder()
                     .contract(new JAXRSContract())
                     .target(GitHub.class, "https://api.github.com");

@GET代表请求方式。@Path代表请求路径,@PathParam ,@QueryParam()代替了@Parm,@PathParam代表获取路径上的参数,@QueryParam(“fack”)获取请求参数上的参数,@Produces指定返回值类型等等,可以参见JAX-RS 规范

Ribbon集成
<!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-gson -->
<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-ribbon</artifactId>
    <version>8.18.0</version>
</dependency>

RibbonClient 重写了 Feign 客户端的对URL的处理,其添加了 智能路由以及一些其他由Ribbon提供的弹性功能。
集成Ribbon需要你将ribbon的客户端名称当做url的host部分来传递

MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://RibbonClient");
Hystrix集成
<!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-gson -->
<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-hystrix</artifactId>
    <version>8.18.0</version>
</dependency>

HystrixFeign 配置了 Hystrix 提供的熔断机制。
要在 Feign 中使用 Hystrix ,你需要添加Hystrix模块到你的环境变量,然后使用 HystrixFeign 来构造你的API:

MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd");
Spring Cloud Feign集成

Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign
SpringCloudFeign呢同样同样也是定义一个接口即可

@FeignClient(url="Server",value="helloServiceClient")
public interface HelloService{
	@RequestMapping("getUser")
	User getUserById(@RequestParam(value="id")Integer id);
}

如果只是单独使用feign功能不配合Eureka的情况下,需要在注解中配置url=“Server” 这个参数是目标主机的server,value是这个接口的定义的bean的name。

Feign的高级用法可以使用继承,这个一般配合Eureka使用,定义一个Feign的service接口,并且写一个实现类直接把该类标记为@RestController,在feign调用的的Server端则直接引入依赖继承该接口即可,feign的Client端直接调用,继承的优点就不再累述了,就直接说一下项目中使用的时候出现的问题吧,在开发中多端调用的情况很多,这就需要开项目开发前期的时候就做好几口的设计,应为当基类一旦被修改,那么各个项目组的代价是很大的。。。

当Feign出现的时候。Ribbon是伴生出现的。当@FeignClient(url=“Server”,value=“helloServiceClient”)出现的时候,会创建一个feign客户端也会创建一个Ribbon客户端。Feign的默认是由超时重试功能的,但是这个超时会有俩个影响,其一,超时回应其Ribbon的retry机制,还有就是会引起熔断。故而在项目开发过程中,切记要把Hystrix的超时时间设置大于Ribbon的超时时间,不然你就会看到服务被降级,而不是重试了。你开发想测试Ribbon的超时重试可以吧Hystrix熔断机制关闭,
feign.hystrix.enabled=false,至于熔断机制的我会在下一篇Hystrix原理解析中详细阐述,
在这里你先可以记住,可以写一个实现Feign接口的实现类定义启动熔断注解就可以实现在方法调用不通时候。返回降级的数据,从而不会导致整个项目进入不可用状态。
使用方式
@FeignClient(url=“Server”,value=“helloServiceClient”,fallback=“CallbackHelloServer.class”) 此外feign在SpringCloud中的集成依旧包括上述原生netflix的配置日志,加密解密,还有一点项目中优化比较常用的是对feign调用的数据启用gzip压缩

feign.cpmression.request.enabled=true;
feign.cpmression.response.enabled=true;