一、Feign简介
1.1、Feign是什么?
Feign是一个声明式(也就是通过注解编写)的Web服务客户端,使得编写Web服务客户端变得非常容易,值需要创建一个接口,然后在上面添加注解即可。
官网:https://github.com/OpenFeign/feign
1.2、Feign能干什么?
Feign旨在使编写Java Http客户端变得更容易。
在使用Spring Cloud Ribbon + RestTemplate时,利用RestTemplate对http请求的封装处理,形成一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多次调用,所以通常都会针对每个微服务自行封装一些客户端类来保证这些依赖服务的调用。
Spring Cloud Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务借口的定义。在Spring Cloud Feign的实现下,我们只需创建一个接口,并使用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时自动封装服务调用客户端的开发量。
Spring Cloud Feign具备可插拔的注解支持,包括Feign注解和JAX-RS注解,同时还扩展了Spring MVC的注解支持。同时集成Ribbon和Eureka,以在使用Feign时提供Http客户端的负载均衡;继承了Hystrix以提供熔断、降级的功能。
另外,对于Feign自身的一些主要组件,比如编码器和解码器等,都支持可插拔的方式,在有需要的时候可以方便地扩展和替换他们。
1.3、Feign的特性
1、可插拔的注解支持,包括Feign注解和JAX-RS注解
2、支持可插拔的HTTP编码器和解码器
3、支持Hystrix和他的Fallback
4、支持Ribbon的负载均衡
5、支持HTTP请求和响应压缩
二、Feign的Hello World
2.1、Feign加入依赖jar
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.2、Feign启动类添加注解
启动类需要开启Feign注解:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
2.3、定义接口
@FeignClient(value = "HelloInterface")
public interface UserService {
@RequestMapping(value = "/userServiceProvider", method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
2.4、配置文件
配置文件类似于Ribbon的配置:
application.properties示例:
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
server.port=8770
spring.application.name=service-feign
2.5、调用服务的Controller类
1、Feign集成了Ribbon,依然是利用了Ribbon维护了userService的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign值需要定义服务绑定接口,以声明式的方法,优雅而简单的实现了服务调用。
2、Spring Cloud Feign值支持Spring MVC 3.x系列,Spring MVC 4.x出现的一系列注解比如@GetMapping, @PostMapping, @PutMappting等Feign是不支持的。
示例:
@RestController
public class HelloController {
@Autowired
private HelloInterface hi;
@RequestMapping("/hi")
public String hi(@RequestParam(value="name") String name){
System.out.println("now in client :" + name);
String res = hi.userServiceProvider(name);
return res;
}
}
这样只需要注入接口,调用远程接口,就只需要调用本地的接口的方法。
这样屏蔽了远程调用的细节,远程接口的代理实际上在远程。
从而Feign简化了远程调用的流程,所以远程调用接口的场景,可使用Feign来做。
而且Feign自带支持Ribbon负载均衡。通过Eureka来做服务发现。
2.6、Feign中Ribbon的配置
由于Spring Cloud Feign的客户端负载均衡是通过Spring Cloud Ribbon来实现的,所以可以直接通过配置Ribbon客户端的方式来自定义各个服务客户端调用的参数
2.7、调用流程
客户端调用 --> Ribbon去选服务端 --> Hystrix调用 --> 远程服务
三、Feign的参数绑定
1、@RequestParam : 绑定单个请求参数
2、@PathVariable :绑定URI模板变量值
3、@RequestHeader:绑定请求头数据
4、@RequestBody:绑定请求的内容区数据,并能进行自动类型转换等。
注意:
在定义各参数绑定的时候,@RequestParam和@RequestHeader等可以指定参数名称的注解,他们的value值千万不能少。在Spring MVC中,这些注解会根据指定参数名来做默认值,但在Feign中绑定参数必须通过value属性来指明具体的参数名,不然会抛出IllegalStateException异常,value属性不能为空。
四、Feign的配置
4.1、Feign的默认配置
Spring Cloud的Feign的一个中心概念就是客户端。每个Feign客户端都是组合的组件的一部分,他们一起工作以按需调用远程服务器,并且该集合具有将其作为使用@FeignClient注释的参数名称。
Spring Cloud使用FeignClientsConfiguration创建一个新的集合,作为每个命名客户端的ApplicationContext(应用上下文),这包含feign.Decoder, feign.Encoder和feign.Contract。
Spring Cloud Netflix默认为Feign提供以下bean:
1、Decoder feignDecoder:ResponseEntityDecoder(其中包含Spring Decoder)
2、Encoder feignEncoder:SpringEncoder
3、Logger feignLogger:Slf4jLogger
4、Contract feignContract:SpringMvcContract
5、Feign.Builder feignBuilder:HystrixFeign.Builder
6、Client feignClient:如果Ribbon启用,则为LoadBalancerFeignClient,否则将使用默认的feign客户端
可以自定义FeignClientsConfiguration以顽强控制这一系列的配置。
4.2、Feign的自定义配置
写一个自定义配置类,注意不要放到当前ComponentScan的范围下:
@Configuration
public class MyConf {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
定义的时new feign.Contract.Default(), 所有在UserService接口中只能使用Feign自己的注解url方式,使用Spring MVC的注解就会报错。
写好配置后,通过设置@FeignClient的configuration来使用,如:
@FeignClient(value=“userService”, configuration=MyConf.class)
可以为每个Feign客户端都配置自己的默认配置。
五、@FeignClient注解
@FeignClient标签的常用属性如下:
1、name(value):指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。
2、url:url一般用于调试,可以手动指定@FeignClient调用的地址。
3、configuration:Feign的配置类,可以自定义Feign的Encoder, Decoder, LogLevel, Contract。
4、fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口,并使用@Component注解。
5、fallbackFactory:工厂类,用于生产fallback类实例,通过这个属性,我们可以实现每个接口通用的容错逻辑,减少重复的代码。
6、path:定义当前FeignClient的统一前缀,添加到Feign访问服务的访问路径上。
7、decode404:当发生http 404错误时,如果该字段为true, 会调用decoder运行解码,否则抛出FeignException。
注意:serviceId属性已被弃用,有利于name属性
以前,使用url属性,不需要name属性,现在需要使用name属性。
六、Feign的@Primary
当使用Feign与Hystrix回退时,在同一类型的ApplicaitonContext中有多个bean。这将导致@Autowired不起作用,因为没有一个bean标记为主。
为了解决这个问题,Spring Cloud Netflix将所有Feign实例标记为@Primary,所以Spring framework将指导要注入哪个bean。在某些情况下,这可能是不可取的。要关闭此行为,将@FeignClient的primary属性设置为false, 如:
@FeighCLient(name=“hello”, primary=false)
七、自定义Feign的HttpClient
Feign在默认情况下使用过的是JDK原生的URLConnection发送Http请求,没有连接池,但是对每个地址会保持一个长连接,即利用http的persistence connection。
可以用Apache的Http client替换Feign原始的http client,从而获取连接池、超时时间等于性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
然后在application.properties中添加:
feign.httpclient.enabled=true
八、自动以编码器和解码器
8.1、自定义Encode, Decode, ErrorDecode
Feign将方法签名中的方法参数对象序列化为请求参数,放到HTTP请求的过程中,是由编码器(Encoder)完成的。同理,将HTTP想用数据反序列化为java对象,是由解码器(Decoder)完成的。
默认情况下,Feign会将表用@RequestParam注解的参数转换为字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。
在Spring Cloud环境下,Feign的Encoder只会用来编码没有添加注解的参数。如果你自定义了Encoder,那么只有在编码没有注解参数的时候,才会调用你的Encoder。
对于Decoder,默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不再200~300之间的ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据http响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常一共调用者处理。
九、FeignClient的工作过程
FeignClient相当于Spring Cloud的RPC,实现过程如下:
1、首先通过@EnableFeignClients注解开启FeignClient
2、根据Feign的规则实现接口,并加@FeignClient注解
3、程序启动后,会进行包扫描,扫描所有有@FeignClient注解的类,并将这些信息注入到IOC容器中。
4、当接口的方法被调用,通过JDK的代理,来生产具体的RestTemplate。
5、RequestTemplate再生产调用的Request
6、Request交给Client去处理,其中Client可以是HttpUrlConnection,HttpClient,也可以是Okhttp。
7、最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡,真正发送请求出去。
十、使用继承特性减少代码冗余
当使用Spring MVC的注解来绑定服务接口的时候,几乎可以完全从服务提供方的Controller中复制操作,构建出响应的服务客户端绑定接口。计入存在这么多复制操作,我们自然需要考虑这部分内容是否可以得到进一步的抽象。Spring Cloud Feign中,针对该问题提供了继承特性来帮助解决这些复制操作,以进一步减少编码量。
基本方法,就是把接口提出来,把model提出来,然后provider和client都使用这个工程:
Provider这边在Controller中不再包含以往会定义的映射注解@RequestMapping,而参数的注解定义在重写的时候自动带过来了,这个类中,出来要实现接口逻辑之外,只需要增加了@RequestController注解使该类成为一个REST接口类。
Consumer这边直接注入Feign标注的接口,完全像调用本地接口一样。
注意:
1、Figh接口只能支持一层继承,而且不能多继承
2、官方不建议在服务器和客户端直接共享接口,因为他引入了紧耦合,并且实际上并不适用于当前形势的Spring MVC(方法参数映射不被继承)
十一、Hystrix配置
11.1、概述
Spring Cloud Feign中集成了Hystrix, Spring Cloud Feign客户端的方法都封装到Hystrix命令中进行服务保护。
11.2、全局配置
直接适用它的默认配置前缀hystrix.command.default就可以进行配置,比如设置全局的超时时间,Hystrix默认的超时时间是1秒:
hystrix.command.default.exection.isolationtrhead.timeoutInMilliseconds=5000
在对hystrix进行设置之前,需要确认Feign的Hystrix的功能是开启的。
11.3、禁用Hystrix
可以通过设置feign.hystrix.enabled为false,或者使用hystrix.command.default.exection.timeout.enabled=false来关闭熔断功能。
如果不想全局关闭Hystrix支持,而只想针对某个服务客户端关闭Hystrix支持时,需要通过使用@Scope(“prototype”)注解为指定的客户端配置Feigh.Builder实例,如:
@Configuration
public class DisableHystrixConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return feign.builder();
}
}
在该服务的Feign接口中引入该配置:
@FeignClient("value=“user.service”,configuration=DisableHystrixConfiguration.class)
11.4、指定命令配置
在实际应用中可能会根据实际业务情况指定出不同的配置方案,可采用hystrix.command.<commandKey>作为前缀,而<commendKey>默认情况下,会采用feign客户端中的方法名作为表示。
需要注意的时,由于方法名有可能会重复,这个时候相同的方法名的hystrix配置会共用,所以在进行方法与配置的时候需要做好统一的规划。
11.5、服务降级配置
Spring Cloud Feign在定义服务客户端的时候,与Spring Cloud Ribbon有很大的差别,由于HystrixCommand定义被封装,无法通过@HystrixCommand注解的fallback参数那也来指定具体的服务降级处理方法。Spring Cloud Feign提供了另外一种简单的方式:
定义一个Feign客户端的服务降级类UserServiceFallback,实现UserService接口,其中每个重写方法的实现逻辑都可以用来定义响应的服务降级逻辑;在服务绑定接口中,通过@FeignClient注解的fallback属性来定制对应的服务降级实现类。
十二、Feign的请求压缩
Spring Cloud Feign支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,值需要通过下面的两个参数设置,就能开启请求与响应的压缩功能:
feign.compression.reqeust.enabled=true
feign.compression.response.enabled=true
同时,还可以对请求压缩做一些更细致的设置,比如下面的配置内容指定压缩的请求数量类型,并设置了请求压缩的大小下线,只有超过这个大小的请求才会进行压缩:
feign.compression.request.enabled=true
feign.compression.request.mime-type=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
十三、Feign的日志配置
Spring Cloud Feign在构建被@FeignClient注解修饰的服务客户端时,回味每一个客户端都创建一个feign.Logger实例,可以配置logging.level.<feignClient>的参数配置格式来开启指定feign客户端的debug日志,其中<feignClient>为feign客户端定义接口的完整路径,如:
logging.level.com.test111.springcloud_feign_api.UserService2=debug
注意:只添加该配置还无法实现对debug日志的输出。这是因为feign客户端默认的Logger.Level对象定义为NONE级别,该级别不会记录任何Feign调用过程中的信息,所以需要调整它的级别,针对全局的日志级别,可以在应用主类中加入:
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
也可以实现配置类,然后在Feign客户端来指定配置类以实现不同的日志级别。
对于Feign的Logger级别注意有下面4类,可根据十几需要进行调整使用:
1、None:不记录任何信息
2、basic:仅记录请求方法,url以及相应状态码和执行时间。
3、headers:除了记录basic级别的信息之外,还会记录请求和响应头信息
4、FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据等。