文章目錄
前情提要:在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