Feign是Netflix开源的声明式HTTP客户端
2 hello world
2.1 加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2 加注解
在SpringBoot的启动类上加@EnableFeignClients注解,如果SpringBoot无法扫描到(当前包及其子包)Feign的接口需要指定basePackages
@SpringBootApplication
//Feign定义的接口没有在当前项目;是通过依赖引入的接口;需要指定扫描路径
@EnableFeignClients(basePackages = {"com.nobug.user.client"})
public class ClientApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ClientApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
}
2.3 写配置
无需配置文件
2.4 写接口
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* 添加用户
*/
@PostMapping("/user/")
UserDTO addUser(@SpringQueryMap UserDTO user);
/**
* 查找用户
* http://user-center/user/{userName}
*/
@GetMapping("/user/{userName}")
UserDTO getUser(@PathVariable("userName") String userName);
/**
* 查找所有
*/
@GetMapping("/user/")
UserDTO allUser();
}
3 Feign的组成
//feign.Client.Default#convertAndSend
HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
URL url = new URL(request.url());
//HttpURLConnection去发送请求 无连接池 无资源管理的概念
//性能不是很好
HttpURLConnection connection = this.getConnection(url);
if (connection instanceof HttpsURLConnection) {
HttpsURLConnection sslCon = (HttpsURLConnection)connection;
if (this.sslContextFactory != null) {
sslCon.setSSLSocketFactory(this.sslContextFactory);
}
if (this.hostnameVerifier != null) {
sslCon.setHostnameVerifier(this.hostnameVerifier);
}
}
//org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient
public class LoadBalancerFeignClient implements Client {
static final Options DEFAULT_OPTIONS = new Options();
//用到了代理模式
private final Client delegate; //默认:带有负载均衡的 Client.Default(null, null) 也会用URLConnection发送请求【后面的优化手段就是引入连接池这些】
//org.springframework.cloud.openfeign.ribbon.DefaultFeignLoadBalancedConfiguration
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);
}
}
4 指定日志级别
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] <--- HTTP/1.1 200 (1788ms)
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] content-length: 17
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] content-type: text/plain;charset=UTF-8
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] date: Tue, 07 Apr 2020 01:40:03 GMT
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info]
2020-04-07 09:40:03.482 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] 192.168.56.1:8764
2020-04-07 09:40:03.483 DEBUG 4060 --- [nio-9761-exec-1] c.n.u.client.UserCenterInfoFeignClient : [UserCenterInfoFeignClient#info] <--- END HTTP (17-byte body)
4.1 细粒度配置
4.1.1 Java代码配置
@FeignClient(name="user-center", configuration = UserCenterFeignConfiguration.class)
public interface UserCenterInfoFeignClient {
/**
* 返回服务器的ip和端口信息
*/
@GetMapping("/user/info")
String info();
}
/**
* 自定义日志级别
* 这个类别加@Configuration注解了,否则必须挪到@ComponentScan能扫描到的包以外,不然就成为全局配置了【父子上下文重复扫描】
*/
public class UserCenterFeignConfiguration {
@Bean
public Logger.Level level(){
//打印所有请求的细节
return Logger.Level.FULL;
}
}
logging:
level:
com.nobug.user.client.UserCenterInfoFeignClient: debug
4.1.2 配置文件
feign.client.config.<feignName>.loggerLevel: full
feign:
client:
config:
user-center:
loggerLevel: full
4.2 全局配置
4.2.1 Java代码配置
方式一:让父子上下文ComponentScan重叠(强烈不建议使用)方式二【唯一正确的途径】: @EnableFeignClients(defaultConfiguration=xxx.class)
@EnableFeignClients(basePackages = {"com.nobug.user.client"}, defaultConfiguration = GlobalFeignConfiguration.class) //全局配置
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ClientApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
}
public class GlobalFeignConfiguration {
@Bean
public Logger.Level level(){
//打印所有请求的细节
return Logger.Level.FULL;
}
}
4.2.2 配置文件
将想要调用的微服务名称换成default就可以实现属性方式的全局配置
feign.client.config.<feignName>.loggerLevel: full
feign:
client:
config:
default:
loggerLevel: full
5 支持的配置项
5.1 代码方式
配置项 | 作用 |
---|---|
Feign.Builder | Feign的入口 |
Client | Feign底层用什么去请求 |
Contract | 契约,注解支持 |
Encoder | 编码器,用于将对象转换成HTTP请求消息体 |
Decoder | 解码器,将响应消息体转换成对象 |
Logger | 日志管理器 |
Logger.Level | 指定日志级别 |
Retryer | 指定重试策略 |
ErrorDecoder | 指定错误解码器 |
Request.Options | 超时时间 |
Collection<RequestInterceptor> | 拦截器 |
SetterFactory | 用于设置Hystrix的配置属性,Feign整合Hystrix才会用 |
5.2 属性方式
6 配置最佳实践总结
6.1 Ribbon配置 vs Feign配置
6.2 Feign代码方式 vs 属性方式
配置方式 | 优点 | 缺点 |
---|---|---|
代码配置 | 基于代码,更加灵活 | 如果Feign的配置类加了Configuration注解,需注意父子上下文;线上修改得重新打包、发布 |
属性配置 | 易上手;配置更加直观;线上修改无需重新打包、发布;优先级更高 【全局代码 < 全局属性 < 细粒度代码 < 细粒度属性】 | 极端场景下没有代码配置方式灵活 |
6.3 最佳实践
- 尽量使用属性配置,属性方式实现不了的情况下再考虑用代码配置
- 在同一个微服务内尽量保持单一性,比如统一使用属性配置,不要两种方式混用,增加定位代码的复杂性
7 Feign的继承
- 官方观点:不建议使用
- 业界观点:很多公司使用
It is generally not advisable to share an interface between a server and a client. It introduces tight coupling, and also actually doesn’t work with Spring MVC in its current form (method parameter mapping is not inherited).
8 多参数请求构造
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* ~/user/q?username=1&password=12346
*/
@GetMapping("/user/q")
UserDTO query(@SpringQueryMap UserDTO user);
}
如果有两个以上的FeignClient接口name相同会报错
spring:
main:
allow-bean-definition-overriding: true
9 Feign脱离Ribbon使用
/**
* 脱离ribbon的使用
* name必须有,随便取
*/
@FeignClient(name = "baidu", url = "www.baidu.com")
public interface BaiduFeignClient {
@GetMapping("")
String getIndexHtml();
}
10 RestTemplate vs Feign
角度 | RestTemplate | Feign |
---|---|---|
可读性、可维护性 | 一般 | 极佳 |
开发体验 | 欠佳 | 极佳 |
性能 | 很好 | 中等 (RestTemplate的50%左右) |
灵活性 | 极佳 | 中等 (内置功能可满足绝大多数需求) |
如何选择?
原则:尽量用Feign,杜绝使用RestTemplate
11 Feign性能优化
11.1 连接池【提升15%左右】
加依赖
<!--httpclient-->
<!--feign优化-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
写配置
feign:
httpclient:
max-connections: 200 # feign的最大连接数
max-connections-per-route: 50 # feign单个路径最大的连接数
enabled: true # 让feign使用apache httpclient发送请求,而不是默认的urlConnection
okhttp
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.9</version>
</dependency>
feign:
httpclient:
max-connections: 200
max-connections-per-route: 50
okhttp:
enabled: true