文章目录
前情提要:在nacos上注册了content-center和user-center两个服务,content-center使用Feign调用user-center服务,使用Ribbon做负载均衡
1.RestTemplate VS Feign
- Feign让我们的代码可读性、可维护性极佳,这是决胜点
- Feign 的唯一短板性能只有RestTemplate的一半,但是也有优化提升的空间
- 所以尽量使用 Feign
- 然而事无绝对,合理选择
2.Feign的组成
我们需要关注的几个部分
- Client 可以自定义请求Client,提高性能
- Logger 默认是不打印日志,所以这里需要配置
- RequestInterceptor 拦截器,很有用,例如给每个请求加Header
3.项目添加Feign
content-center 项目添加Feign组件,依然三板斧
3.1 加依赖
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.2 加注解
启动类ContentApplication添加注解@EnableFeignClients
@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients
public class ContentApplication {
public static void main(String[] args) {
SpringApplication.run(ContentApplication.class, args);
}
}
3.3 写配置
没有必须写的配置
4.项目使用Feign
4.1 声明 FeignClient(UserCenterFeignClient)
新建个包feignclient下面新建接口UserCenterFeignClient
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "user-center")
public interface UserCenterFeignClient{
/**
* FeignClient的name + GetMapping的value
* 相当于 http://user-center/reciteHis/testAno
* 和RestTemplate里写的url一模一样
* @return Page
*/
@GetMapping(value = "/reciteHis/testAno")
Page memberRctHisAno();
}
4.2 使用UserCenterFeignClient
重构Controller里的方法,代码很简洁,并且从client接口里知晓每个方法是做什么的,可读性变强了
@Autowired
private UserCenterFeignClient userCenterFeignClient;
@GetMapping(value = "testRibbon")
public Object testRibbon() {
//1. 未使用Ribbon时
// List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
// String targetUrl = instances.stream()
// .map(instance -> instance.getUri().toString() + "/reciteHis/testAno")
// .findFirst()
// .orElseThrow(() -> new IllegalArgumentException("当前没有实例!"));
// 2.Ribbon + restTemplate
// String targetUrl= "http://user-center/reciteHis/testAno";
// Page page = restTemplate.getForObject(targetUrl, Page.class);
// 3.Feign
Page page = userCenterFeignClient.memberRctHisAno();
return ResponseVO.success(page);
}
4.3 测试
请求content-center的http://localhost:8082/reciteHis/testAno。使用Feign成功
5.Feign的日志级别
开发环境推荐使用FULL,生产环境推荐BASIC
5.1 java代码方式自定义日志级别
- 设置UserCenterFeignClient的项目(不是Feign的日志,注意这两个日志的概念区别)日志级别为debug
只有这个文件的日志级别设置为debug,Feign的日志才能打印出来,这是先决条件
logging:
file: D://springlog/content/log.log
level:
com.zengchen.content.feignclient.UserCenterFeignClient: debug
- 新建UserCenterFeignClientConfiguration
和Ribbon的java代码配置一样,写个配置类,返回日志级别。注意这里的Logger,得是feign包下面的Logger,别引用错了
如果你加了@Configuration,也会产生父子上下文问题,变成了Feign的全局配置
所以,要么别加@Configuration注解,要么把这个配置类放到一个启动类扫描不到的包里,like ribbon的配置类
这里推荐不加@Configuration注解的方式,因为简单啊。
那么ribbon的配置类问什么不采用这种不加注解的方式呢?因为ribbon的配置类必须加@Configuration,不加不行啊,所以ribbon没办法像feign一样
import feign.Logger;
import org.springframework.context.annotation.Bean;
//@Configuration
public class UserCenterFeignClientConfiguration {
@Bean
public Logger.Level loggerLevel(){
//全日志
return Logger.Level.FULL;
}
}
- UserCenterFeignClient使用UserCenterFeignClientConfiguration
configuration = UserCenterFeignClientConfiguration.class
@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
public interface UserCenterFeignClient {
/**
* FeignClient的name + GetMapping的value
* 相当于 http://user-center/reciteHis/testAno
* 和RestTemplate里写的url一模一样
* @return Page
*/
@GetMapping(value = "/reciteHis/testAno")
Page memberRctHisAno();
}
- 重启项目测试
请求 http://localhost:8081/poem/testRibbon,可以看到content-center里的日志,feign的日志都打印出来了
5.2 属性配置方式自定义日志级别
- 先把UserCenterFeignClient的注解注释掉
//@FeignClient(name = "user-center",configuration = UserCenterFeignClientConfiguration.class)
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* FeignClient的name + GetMapping的value
* 相当于 http://user-center/reciteHis/testAno
* 和RestTemplate里写的url一模一样
* @return Page
*/
@GetMapping(value = "/reciteHis/testAno")
Page memberRctHisAno();
}
- 写配置
logging:
file: D://springlog/content/log.log
level:
com.zengchen.content.feignclient.UserCenterFeignClient: debug
feign:
client:
config:
# 想要调用的微服务的名称
user-center:
loggerLevel: basic
- 重启项目测试
请求 http://localhost:8081/poem/testRibbon,可以看到content-center里feign的basic日志比full要少很多
6.Feign的全局配置
上小节的日志配置,都是只针对于user-center服务的起作用,如果content-center还要调用比如,短信服务,广告服务等等,就看不到feign的日志了,因为feign默认不打印日志,全局配置就是content-center调用其它所有的服务都起作用的配置
6.1 java代码方式
利用父子上下文ComponentScan的bug
这虽然是一种方式,但是这是一种病态的方式,强烈不建议使用- 启动类@EnableFeignClients的defaultConfiguration属性
注释application.yml里的配置
#feign:
# client:
# config:
# # 想要调用的微服务的名称
# user-center:
# loggerLevel: basic
给@EnableFeignClients添加
defaultConfiguration = UserCenterFeignClientConfiguration.class属性,这样UserCenterFeignClientConfiguration就变成了全局的配置,不再仅限于调用user-center的时候
@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {
public static void main(String[] args) {
SpringApplication.run(ContentApplication.class, args);
}
}
- 重启项目测试
请求 http://localhost:8081/poem/testRibbon,又变成full日志了
6.2 属性配置方式
- 注释java代码全局配置
@SpringBootApplication
@MapperScan("com.zengchen.content.mapper")
@EnableFeignClients//(defaultConfiguration = UserCenterFeignClientConfiguration.class)
//@EnableFeignClients(basePackages = "com.zengchen.user.client")
public class ContentApplication {
public static void main(String[] args) {
SpringApplication.run(ContentApplication.class, args);
}
}
- 修改属性配置
把原来的user-center改成default就行
feign:
client:
config:
# 全局配置
default:
loggerLevel: basic
- 重启项目测试
请求 http://localhost:8081/poem/testRibbon,又变回basic日志了
6.3 java代码方式 vs 属性配置方式
这两种方式所支持的配置项是不同的 !!
- java代码方式支持的配置项
- 属性配置方式支持的配置项
7.配置实践总结
7.1 Ribbon配置 vs Feign配置
7.2 Feign代码方式 vs 属性方式
我测试了一下,Ribbon代码方式比属性方式优先级高,Feign代码方式比属性方式优先级低
7.3 最佳配置搭配推荐
- 尽量使用属性配置,属性配置实现不了再考虑用代码配置
- 同一个微服务尽量保持配置方式单一性,不要两种配置方式混用,会增加定位代码问题的复杂性
8.多参数请求构造
参考大目老师手记:https://www.imooc.com/article/289000
9.Feign请求非注册服务的接口
这种方式不会使用到Ribbon,所以也叫Feign脱离Ribbon的使用方式
- 重新定义一个 TestImoocFeignClient
@FeignClient(name = “getImooc”,url = “www.baidu.com”),name随便自己定义,url就是你想请求的链接。关键就是这个url参数
//@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")
//@FeignClient(name = "getImooc",url = "www.sogou.com")
//@FeignClient(name = "getImooc",url = "www.imooc.com")
@FeignClient(name = "getImooc",url = "www.baidu.com")
public interface TestImoocFeignClient {
@GetMapping(value = "")
String index();
}
- 新建 TestImoocController 使用TestImoocFeignClient里的index方法
@RestController
public class TestImoocController {
@Autowired
TestImoocFeignClient testImoocFeignClient;
@GetMapping(value = "getImooc")
public String imoocIndex(){
return this.testImoocFeignClient.index();
}
}
- 重启测试
访问 http://localhost:8081/getImooc,FeignClient里的url=www.baidu.com,所以我们要访问的是baidu
- 其它url测试结果
TestImoocFeignClient的@FeignClient url参数,我换成 www.imooc.com,或者www.csdn.net,或者www.so.com,都会报错 301 Moved Permanently错误,只有www.baidu.com得到了预期返回。
修改user-center的配置,把user-center变成非nacos注册服务时,用Feign访问user-center的请求,是可以访问的
@FeignClient(name = "getImooc",url = "localhost:8082/reciteHis/1")
10 Feign性能优化
Feign的性能只有RestTemplate的50%,但是不用为Feign担心,一般项目的瓶颈不会发生在Feign上,优化仅是为了更好!
10.1 优化原理
第二节 Feign的组成里有Client组件,就是优化这个Client
- 不和Ribbon配合使用的时候,Client 是默认的Feign.client.default ,这个default里使用的是HttpURLConnection,这个HttpURLConnection,不支持连接池,所以性能不高
- 和Ribbon配合使用的时候,Client 使用的是LoadBalancerFeignClient,支持代理模式,默认的也是Feign.client.default
从上图可以看出,还有两个构造client的类,一个是HttpClient,一个是OKHttp。优化就是用它们替换default
10.2 使用HttpClient优化
- 引入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 写配置
feign:
client:
config:
# 全局配置
default:
loggerLevel: basic
httpclient:
# 让feign使用apache httpclient做请求,而不是默认的httpurlconnection
enabled: true
# feign 最大连接数
max-connections: 200
# feign 单个路径的最大连接数
max-connections-per-route: 50
10.3 使用OKHttp优化
- 引入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<!--<version>10.3.0</version>-->
</dependency>
- 写配置
feign:
client:
config:
# 全局配置
default:
loggerLevel: basic
okhttp:
enabled: true
httpclient:
# 让feign使用apache httpclient做请求,而不是默认的httpurlconnection
# enabled: true
# feign 最大连接数
max-connections: 200
# feign 单个路径的最大连接数
max-connections-per-route: 50
max-connections和max-connections-per-route使用的是httpclient里面的
11 Feign常见问题总结
参考大幕老师手记:https://www.imooc.com/article/289005
12 Feign的继承特性
https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-inheritance
12.1 user-center项目拆分成三个模块
这个可以参照我的另一篇博文 :
https://blog.csdn.net/hantangduhey/article/details/99306490