一、Feign的介绍
Feign是一个声明式 WebService 客户端,使用Feign能够让编写Web Service 客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可插拔式的编码器和解码器。
Spring Cloud 对 Fiegn 进行了封装,使其支持了Spring MVC 标准注解和HttpMessageConverts。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
前面使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法,但是在实际的开发中,由于对服务依赖的调用可能不止一处。往往一个接口会被多处调用,所以通常会对每个微服务自行封装一些客户端类来包装依赖服务的调用,所以Feign在此基础上做了进一步封装,有他来帮助我们自定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置他(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成服务提供放的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
二、Feign的使用
通常我们会把 feign 的定义作为一个单独的工程,以供其他服务调用;
1、feign的项目
(1)依赖 feign 的
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
(2)修改打包方式
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> </plugin> </plugins> </build>
(3)编写声明式接口
@FeignClient("product-center")
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product-center/v1/person/{id}
* @param id
* @return
*/
@RequestMapping("/v1/person/{id}")
Person getPerson(@PathVariable("id") String id);
}
2、调用 feign 的项目
order-center 服务调用 product-center 服务;
(1)引入上面定义的 feign 项目的依赖
<dependency> <groupId>com.yufeng</groupId> <artifactId>cloud-feign-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
(2)启动类增加 @EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
(3)order-center 服务调用 product-center(像调用本地方式一样调用远程服务)
@RestController public class OrderController { @Autowired private ProductCenterFeignApi productCenterFeignApi; @GetMapping("/v1/getProduct/{id}") public String getProduct(@PathVariable("id") String id) { Person person = productCenterFeignApi.getPerson(id); return person.toString(); } }
3、Feign的自定义配置
在默认情况下,Feign的调用是不打印日志;需要我们通过自定义来打印 feign 日志。
(1)feign 日志的级别定义
- NONE(默认):不记录任何日志;
- BASIC: 仅打印请求方法、URL、响应状态码、执行时间;(生产环境推荐)
- HEADERS: 记录 BASIC 级别的基础上,记录请求和响应的 header;
- FULL: 记录请求和响应的 header、body和元数据;
(2)feign日志的定义
方法一:配置类的方式,并指定 @FeignClient 注解的 configuration 属性
<1> 定义一个 feign 的配置类(注意:不可以被包扫描到(不可加@Configuration注解),否则会作为全局配置)
public class ProductCenterFeignConfig {
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
<2> 将定义的 feign 配置类配置到对应的位置,如下:
package com.yufeng.feignapi;
@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product-center//v1/person/{id}
* @param id
* @return
*/
@RequestMapping("/v1/person/{id}")
Person getPerson(@PathVariable("id") String id);
}
<3> 调用端工程 order-center 服务,将 com.yufeng.feignapi 包下日志级别定义为 debug,否则是不会打印 feign 的日志的。
logging: level: com: yufeng: feignapi: debug
方法二:配置文件的方式
不用指定 @FeignCilent 注解的 configuration 的选项
# com.yufeng.feignapi 包的日志级别定义为 debug; logging: level: com: yufeng: feignapi: debug # feign的日志级别定义为FULL feign: client: config: product-center: loggerLevel: FULL
打印的日志结果:
4、feign的使用其原生注解配置
我们上面的例子中,feign 的配置使用的 SpringMVC的注解 @RequestMapping;因为自动装配的 FeignCilents 配置中的默认契约是 SpringMVC;如果我们要使用 feign 的原生注解,首先要修改契约的配置。
方式一:使用注解的方式配置
(1)修改feign的契约,在feign的定义 ProductCenterFeignConfig 中增加配置
public class ProductCenterFeignConfig {
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
/**
* 通过修改契约为默认的Feign的锲约,那么就可以使用默认的注解
* @return
*/
@Bean
public Contract feignContract() {
return new Contract.Default();
}
}
(2)将上面定义的配置类,指定给 @FeignClient 的 Configuration 选项。
@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
//@FeignClient(value = "product-center")
public interface ProductCenterFeignApi {
/**
* 修改锲约为Feign的 那么就可以使用默认的注解
* @param id
* @return
*/
@RequestLine("GET /v1/person/{id}")
Person getPerson(@Param("id") String id);
}
方式二:使用配置文件的方式配置
(1)修改默认契约
feign: client: config: product-center: contract: feign.Contract.Default
(2)修改为 feign 的原生注解
@FeignClient(value = "product-center")
public interface ProductCenterFeignApi {
/**
* 修改锲约为Feign的 那么就可以使用默认的注解
* @param id
* @return
*/
@RequestLine("GET /v1/person/{id}")
Person getPerson(@Param("id") String id);
}
因为feign的注解我们不常用,推荐使用 Spring MVC 的注解。
三、Feign的高级使用
1、服务调用的请求头透传
我们使用 PostMan 去调用 product-center 服务携带了 header,product-center的接口又去调用了 order-center 服务,需要把请求头传递给 order-center 服务的。
(1)自定义一个拦截器实现 RequestInterceptor 接口
public class CustomRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(null != requestAttributes) {
HttpServletRequest request = requestAttributes.getRequest();
requestTemplate.header("Token", request.getHeader("token"));
}
}
}
(2)将这个拦截器给配置到feign的配置上, @FeignClient 注解的 configuration 选项上;
public class ProductCenterFeignConfig { @Bean public Logger.Level level() { return Logger.Level.FULL; } @Bean public RequestInterceptor requestInterceptor() { return new CustomRequestInterceptor(); } }
@FeignClient(value = "product-center", configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product-center//v1/person/{id}
* @param id
* @return
*/
@RequestMapping("/v1/person/{id}")
Person getPerson(@PathVariable("id") String id);
}
2、feign的调用优化方案
开启连接池配置,使feign的底层调用使用 HttpClient,这样就有连接池的概念;
feign: client: config: product-center: loggerLevel: BASIC httpclient: # feign的底层去调用HttpClient enabled: true max-connections: 200 #最大连接数 max-connections-per-route: 50 #为每个url请求设置最大连接数