Feign声明式HTTP客户端
1. Feign的定义
Feign是Netflix开源的声明式客户端
解析:声明式HTTP客户端,我们只需要声明一个接口,Feign就会通过我们定义的接口,自动构造请求的目标地址,并完成发送请求
GitHub地址:https://github.com/openfeign/feign
2. 整合Feign
2.1 加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
2.2 加注解
在启动类上面加@EnableFeignClients
注解
3. 实际应用
首先我们之前服务之间的调用使用的是RestTemplate,使用RestTemplate会存在一些问题。所以我们考虑使用Feign代替RestTemplate,接下来我们就实现一下:
我们是内容中心调用用户中心的服务,所以我们在内容中心添加一个用户中心服务的接口类,代码如下:
package com.chuxin.contentcenter.feignclient;
import com.chuxin.contentcenter.dao.dto.UserDTO;
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @FileName: UserFeignClients
* @Description: 用户中心的Feign远程调用接口类
* @author: <a href="mailto: muyuanpei@camelotchina.com">myp</a>
* @create: 2019-08-16 14:17
* @Copyright: (c) 初心科技有限公司(待创)
*/
//name值为用户服务的名称
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
@GetMapping("/users?id={id}")
UserDTO findById(@PathVariable("id") Integer id);
}
然后我们在业务代码里面就可以通过该接口类直接调用就可以了。
@Autowired
private UserCenterFeignClient userCenterFeignClient;
//业务代码里这样直接调用就ok了
UserDTO userDTO = userCenterFeignClient.findById(userId);
通过使用FeignClient的方式相对于使用RestTemplate方式,可以解决以下问题:
- 编码的可读性差
- 复杂的url难以维护
- 难以响应需求的变化,变化很没有幸福感
- 编程体验不统一
4. Feign的组成
Feign的源码里面使用了大量的设计模式,比如建造者模式,代理模式等等。
image.png
1. Feign.Builder:Feign的入口,每一个FeignClient都是由他构建的
2. Client:指定Feign的底层用什么去请求。Feign.Client.Default使用的是用urlconnection去请求的,urlconnection没有连接池,也没有资源管理的概念,性能不是特别好;LoadBalancerFeignClient这个类用到了代理模式,代理了Client接口,这意味着你可以传一个你喜欢的Client过来使用,默认就是Client.Default
3.Contract:注解支持,Feign支持使用SpringMVC的注解,是由SpringMVCContract实现的
5. Feign的细粒度配置
和ribbon一样,两种方式。代码方式和属性方式
以配置Feign的日志打印级别入手学习(默认Feign是不打印任何日志的),spring打印日志级别是在配置文件中配置如下代码就可以了,但是Feign这样配置可不行
logging:
level:
com.chuxin: debug
Feign的四种自定义日志级别
级别 | 打印内容 | 适用场景 |
---|---|---|
NONE(默认值) | 不记录任何日志 | |
BASIC | 仅记录请求方式、url、响应状态码以及执行时间 | 生产环境 |
HEADERS | 记录BASIC级别基础上,记录请求和响应的hearder | |
FULL | 记录请求和响应的header、body和元数据(全部信息) | 开发环境 |
5.1 代码方式
我们举例配置FULL级别的日志打印
首先我们在上面的基础上改一下UserCenterFeignClient.java这个类,在上面的注解添加configuration = xxx.class即可,修改后代码如下:
package com.chuxin.contentcenter.feignclient;
import com.chuxin.contentcenter.config.UserCenterFeignConfig;
import com.chuxin.contentcenter.dao.dto.UserDTO;
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @FileName: UserFeignClients
* @Description: 用户中心的Feign远程调用接口类
* @author: <a href="mailto: muyuanpei@camelotchina.com">myp</a>
* @create: 2019-08-16 14:17
* @Copyright: (c) 初心科技有限公司(待创)
*/
//name值为用户服务的名称
@FeignClient(name = "user-center" ,configuration = UserCenterFeignConfig.class)
public interface UserCenterFeignClient {
@GetMapping("/users?id={id}")
UserDTO findById(@PathVariable("id") Integer id);
}
UserCenterFeignConfig类如下:
package com.chuxin.contentcenter.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
/**
* @FileName: UserCenterFeignConfig
* @Description: 配置Feign日志打印级别的配置类。也可以添加其他的配置
* @author: <a href="mailto: muyuanpei@camelotchina.com">myp</a>
* @create: 2019-08-16 15:13
* @Copyright: (c) 初心科技有限公司(待创)
*/
//@Configuration
public class UserCenterFeignConfig {
@Bean
public Logger.Level level(){
//配置Feign打印所有请求的细节
return Logger.Level.FULL;
}
}
/**
* 注意:
* @Configuration这个注解如果加上就需要将这个类放到启动类扫描不到的包下面,如果不加,这个类就放在启动类可以扫描的包下面
* 如果@Configuration注解使用错误,就会存在父子上下文重复问题
* */
最后在配置文件中添加,如下配置:
# 配置日志打印级别
logging:
level:
# 接口全路径:debug级别日志
com.chuxin.contentcenter.feifnclient.UserCenterFeignClient: debug
这样就配置完成了,在进行调用就会打印Feign的日志了。
5.2 属性方式
这个就简单多了,在文章上面第三点的基础上,在配置文件中添加一行配置就可以搞定了
配置代码如下:
feign:
client:
config:
# 想要调用的微服务名称
user-center:
loggerLevel: full
这样就可以了,完成了和代码配置一样的效果
6. Feign的全局配置
6.1 代码方式
在启动类上面添加注解@EnableFeignClients(defaultConfiguration = UserCenterFeignConfig.class)
然后添加一个全局的配置类就ok了,配置类放在启动类可以扫描的包结构下,配置类代码如下:
package com.chuxin.contentcenter.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
/**
* @FileName: UserCenterFeignConfig
* @Description: 配置Feign日志打印级别的配置类。也可以添加其他的配置
* @author: <a href="mailto: muyuanpei@camelotchina.com">myp</a>
* @create: 2019-08-16 15:13
* @Copyright: (c) 初心科技有限公司(待创)
*/
public class GlobalFeignConfig {
@Bean
public Logger.Level level(){
//配置Feign打印所有请求的细节
return Logger.Level.FULL;
}
}
/**
* 注意:
* 这个注解如果加上就需要将这个类放到启动类扫描不到的包下面,如果不加,这个类就放在启动类可以扫描的包下面
* 如果@Configuration注解使用错误,就会存在父子上下文重复问题
* */
完成!!!
6.2 属性方式
在配置文件中,添加如下配置即可:
feign:
client:
config:
# 将想要调用的微服务名称换成default就可以实现属性方式的全局配置了
default:
loggerLevel: full
7. Feign支持的配置项
使用代码方式和使用属性方式支持的配置项稍有不同
7.1 使用代码方式支持的配置项
配置项 | 作用 |
---|---|
Logger.Level | 指定日志级别 |
Retryer | 指定重试策略 |
ErrorDecoder | 指定错误解码器 |
Request.Options | 超时时间 |
Collection<RequestInterceptor> | 拦截器 |
SetterFactory | 用于设置Hystrix的配置属性,Feign整合Hystrix才会用 |
7.2 使用属性方式支持的配置项
feign:
client:
config:
# 想要调用的微服务名称
default:
loggerLevel: full # 日志级别四种: NONE(默认值)、BASIC、HEADERS、FULL
connectTimeout: 5000 # 连接超时时间
readTimeout: 5000 # 读取超时时间
errorDecoder: com.example.SimpleErrorDecoder # 错误解码器
retryer: com.example.SimpleRetryer # 重试策略
requestInterceptor: com.example.SimpleRequestInterceptor # 拦截器
# 是否对404错误码解码
# 处理逻辑详情见feign.SynchronousMethodHandler#executeAndDecode
decode404: false
encoder: com.example.SimpleEncoder # 编码器
decoder: com.example.SimpleDecoder # 解码器
contract: com.example.SimpleContract # 契约
8. 对比
image.png
image.png
优先级由低到高:全局代码 < 全局属性 < 细粒度代码 < 细粒度属性
9. 配置的最佳实践
-
尽量使用属性配置,属性方式实现不了的在考虑用代码配置
-
在同一微服务内 尽量保证单一性 ,比如同一使用属性配置,不要两种方式混用,增加定位代码的复杂性