Feign 是声明式 HTTP 客户端,它屏蔽了底层 HTTP 的调用过程,让编写 Web 服务客户端更加容易,使用 Feign 只需要创建接口并添加上简单的注解,就可以以面向接口编程的方式发起远程 HTTP 服务调用。Feign 具有可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解。Feign 还支持可插拔编码器和解码器。Spring Cloud 添加了对 Spring MVC 注释的支持,并支持使用 Spring Web 中默认使用的相同 HttpMessageConverters。 Spring Cloud 集成了 Ribbon 和 Eureka 以及 Spring Cloud LoadBalancer,以在使用 Feign 时提供负载均衡的 HTTP 客户端。
通过前面的文章我们已经知道 Ribbon 是一个客户端负载均衡器,而 Feign 是 Spring Cloud 组件中的一个轻量级 RESTful 的 HTTP 客户端,Feign 内置了 Ribbon 。OpenFeign 是 Spring Cloud 在 Feign 的基础上增加了对 Spring MVC 的注解支持,OpenFeign 的 @FeignClient 可以解析SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生代理类,通过代理类发起远程服务调用。
为了模拟使用 OpenFeign 发起远程调用,我们需要准备三个服务,注册中心服务 cloud-eureka-server、提供者服务 cloud-feign-provider、消费者服务 cloud-feign-consumer 。并先启动注册中心服务 cloud-eureka-server 。
提供者服务就是一个普通的微服务,向外提供 Web 服务接口,POM 依赖如下:
<dependencies>
<dependency>
<groupId>com.luke.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
application.yml 文件内容:
server:
port: 8084
spring:
application:
name: cloud-feign-provider
#连接Eureka注册中心
eureka:
instance:
prefer-ip-address: true #使用ip地址注册
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://admin:123456@localhost:7001/eureka
编写一个 Controller 类 FeignProviderController:
@Slf4j
@RequestMapping("/provider")
@RestController
public class FeignProviderController {
@GetMapping("/echo/{id}")
public UserInfo echo(@PathVariable("id") Long id){
log.info("echo,id:{}",id);
return new UserInfo(id,"luke",18,new Date());
}
}
主启动类 CloudFeignProviderMain8084 :
@SpringBootApplication
@EnableDiscoveryClient
public class CloudFeignProviderMain8084 {
public static void main(String[] args) {
SpringApplication.run(CloudFeignProviderMain8084.class,args);
}
}
cloud-feign-consumer 除了常规依赖外,额外添加 openfeign 依赖:
<dependencies>
<dependency>
<groupId>com.luke.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
application.yml 文件内容:
server:
port: 8085
spring:
application:
name: cloud-feign-consumer
#连接Eureka注册中心
eureka:
instance:
prefer-ip-address: true #使用ip地址注册
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://admin:123456@localhost:7001/eureka
定义一个 OpenFeign 接口,使用 OpenFeign 将通过面向接口编程,简化 HTTP 远程调用过程:
@FeignClient(name = "cloud-feign-provider") //指定目标服务的名称
public interface ProviderFeignClient {
@GetMapping("/provider/echo/{id}")
UserInfo echo(@PathVariable("id") Long id); //目标服务接口信息
}
定义一个 Controller 类 FeignConsumerController,通过该 FeignConsumerController 依赖 ProviderFeignClient 发起远程服务调用:
@Slf4j
@RequestMapping("/consumer")
@RestController
public class FeignConsumerController {
/**依赖OpenFeign远程服务接口*/
@Resource
private ProviderFeignClient providerFeignClient;
@GetMapping("/echo/{id}")
public UserInfo consumerEcho(@PathVariable("id") Long id){
log.info("consumerEcho,id:{}",id);
return providerFeignClient.echo(id); //远程服务调用
}
}
主启动类添加 @EnableFeignClients 注解,开启 OpenFeign 功能:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启Feign功能
public class CloudFeignConsumerMain8085 {
public static void main(String[] args) {
SpringApplication.run(CloudFeignConsumerMain8085.class,args);
}
}
使用到的 Bean 类型 UserInfo 放在了 公共依赖模块 cloud-api-commons :
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserInfo {
private Long id;
private String username;
private Integer age;
private Date birthday;
}
先启动注册中心服务,后启动一个提供者服务和两个消费者服务,在浏览器打开注册中心管理台 http://localhost:7001/ :
在浏览器输入地址 http://localhost:8085/consumer/echo/10 连续访问多次,可以在控制台看到会轮询调用两个提供者服务,OpenFeign 使用 Ribbon 去做负载均衡,默认使用轮询负载均衡策略。
Feign 默认情况下是不打印任何调用日志的,在实际开发中尤其是接口联调时,很多时候我们希望查看到 Feign 的日志,以便在出现问题时及时处理。开启 Feign 日志的方式主要有两种:代码方式和配置文件方式,另外每种配置方式还分为局部配置和全局配置。
Feign 的日志级别和 Spring Boot 的不一样,所以不能直接通过配置 Spring Boot 的日志级别来开启。下表是 Feign 日志级别说明:
日志级别 | 打印内容 |
---|---|
NONE(默认) | 不答应任何日志 |
BASIC | 仅记录请求方法、URL、响应状态代码及执行时间 |
HEADERS | 记录 BASIC 级别内容的继承上,还记录请求响应的 HTTP 协议头部信息 |
FULL | 记录整合请求、响应过程的header、body 和 元数据信息 |
定义一个 Feign 配置类 MyFeignConfig ,指定日志级别,内容如下:
public class MyFeignConfig {
@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
}
局部配置方式:使用在某个 Feign 接口上。
@FeignClient(name = "cloud-feign-provider",configuration = MyFeignConfig.class)
public interface ProviderFeignClient {
@GetMapping("/provider/echo/{id}")
UserInfo echo(@PathVariable("id") Long id);
}
在 application.yml 指定该 ProviderFeignClient 日志级别为 debug:
logging:
level:
#这里需要配置为debug,否则feign的日志级别配置不会生效
com.luke.springcloud.feign.ProviderFeignClient: debug
全局配置方式:对所有 Feign 接口生效。
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(defaultConfiguration = MyFeignConfig.class) //开启Feign功能,指定配置类
public class CloudFeignConsumerMain8085 {
public static void main(String[] args) {
SpringApplication.run(CloudFeignConsumerMain8085.class,args);
}
}
application.yml 文件指定 Feign 接口所在包级别为 debug:
logging:
level:
com.luke.springcloud.feign: debug #局部为指定类,全局为所在包
局部配置方式:使用在某个 Feign 接口上。
# Feign日志级别
feign:
client:
config:
cloud-feign-provider:
loggerLevel: full
logging:
level:
com.luke.springcloud.feign.ProviderFeignClient: debug
全局配置方式:对所有 Feign 接口生效。
# Feign日志级别
feign:
client:
config:
default:
loggerLevel: full
logging:
level:
com.luke.springcloud.feign: debug
配置完成后,重启消费者服务 cloud-feign-consumer ,在浏览器输入地址 http://localhost:8085/consumer/echo/10 可以在控制台看到较完整的 HTTP 调用过程日志:
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] <--- HTTP/1.1 200 (259ms)
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] connection: keep-alive
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] content-type: application/json
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] date: Tue, 12 May 2020 06:25:21 GMT
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] keep-alive: timeout=60
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] transfer-encoding: chunked
2020-05-12 14:25:21.753 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo]
2020-05-12 14:25:21.755 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] {"id":10,"username":"luke","age":18,"birthday":"2020-05-12T06:25:21.745+0000"}
2020-05-12 14:25:21.755 DEBUG 128656 --- [nio-8085-exec-1] c.l.s.feign.ProviderFeignClient : [ProviderFeignClient#echo] <--- END HTTP (78-byte body)
文章首先介绍了 OpenFeign 组件,它是一个声明式的 HTTP 客户端,内部整合了 Ribbon 作为其负载均衡实现。接着我们通过一个案例快速将 OpenFeign 整合到项目中,成功发起远程 HTTP 服务调用。最后我们还介绍了 OpenFeign 的日志级别,配置 OpenFeign 日志输出能够在开发调试过程中帮助开发者了解底层 HTTP 的调用过程,快速处理 bug ,生产环境建议关闭。
代码工程地址:https://gitee.com/luke2017/springcloud2020
如果觉得对您有帮助的话,就帮忙给作者公众号点个关注吧,毕竟写作不易,且看且珍惜呀,松弟萌!!!