本節內容帶你認識什么是微服務的特點,微服務的拆分,會使用Nacos實現服務治理,會使用OpenFeign實現遠程調用(通過黑馬商城來帶你了解實際開發中微服務項目)
前言:從谷歌搜索指數來看,國內從自2016年底開始,微服務熱度突然暴漲
那么:到底什么是微服務?
目錄
主要內容
1.認識微服務
1.1.單體架構
1.2.微服務
1.3.SpringCloud
2.微服務拆分
2.1.熟悉黑馬商城
2.1.1.登錄
2.2.2.搜索商品
2.2.3.購物車
2.2.4.下單
2.2.5.支付
2.2.服務拆分原則
2.2.1.什么時候拆
2.2.2.怎么拆
2.3.拆分購物車、商品服務
2.3.1.商品服務
2.4.服務調用
2.4.1.RestTemplate
2.4.2.遠程調用
3.服務注冊和發現
3.1.注冊中心原理
3.2.Nacos注冊中心
3.3.服務注冊
3.3.1.添加依賴
3.3.2.配置Nacos
3.3.3.啟動服務實例
3.4.服務發現
3.4.1.引入依賴
3.4.2.配置Nacos地址
3.4.3.發現并調用服務
4.OpenFeign
4.1.快速入門
4.1.1.引入依賴
4.1.2.啟用OpenFeign
4.1.3.編寫OpenFeign客戶端
4.1.4.使用FeignClient
4.2.連接池
4.2.1.引入依賴
4.2.2.開啟連接池
4.3.最佳實踐
4.3.1.思路分析
4.3.2.抽取Feign客戶端
4.3.3.掃描包
4.4.日志配置
4.4.1.定義日志級別
4.4.2.配置
主要內容
-
知道單體架構的特點
-
知道微服務架構的特點
-
學會拆分微服務
-
會使用Nacos實現服務治理
-
會使用OpenFeign實現遠程調用
1.認識微服務
1.1.單體架構
單體架構(monolithic structure):顧名思義,整個項目中所有功能模塊都在一個工程中開發;項目部署時需要對所有模塊一起編譯、打包;項目的架構設計、開發模式都非常簡單。
當項目規模較小時,這種模式上手快,部署、運維也都很方便,因此早期小項目都采用這種模式。
但隨著項目的業務規模越來越大,團隊開發人員也不斷增加,單體架構就呈現出越來越多的問題:
-
團隊協作成本高:團隊數十個人同時協作開發同一個項目,由于所有模塊都在一個項目中,不同模塊的代碼之間物理邊界越來越模糊。
-
系統發布效率低:任何模塊變更都需要發布整個系統,而系統發布過程中需要多個模塊之間制約較多,需要對比各種文件,任何一處出現問題都會導致發布失敗,往往一次發布需要數十分鐘甚至數小時。
-
系統可用性差:單體架構各個功能模塊是作為一個服務部署,相互之間會互相影響,一些熱點功能會耗盡系統資源,導致其它服務低可用。
1.2.微服務
微服務架構,首先是服務化,就是將單體架構中的功能模塊從單體應用中拆分出來,獨立部署為多個服務。同時要滿足下面的一些特點:
-
單一職責:一個微服務負責一部分業務功能,并且其核心數據不依賴于其它模塊。
-
團隊自治:每個微服務都有自己獨立的開發、測試、發布、運維人員,團隊人員規模不超10人
-
服務自治:每個微服務都獨立打包部署,訪問自己獨立的數據庫。并且要做好服務隔離,避免對其它服務產生影響
那么,單體架構存在的問題有沒有解決呢?
-
團隊協作成本高?
-
由于服務拆分,每個服務代碼量大大減少,參與開發的后臺人員較少,協作成本大大降低
-
-
系統發布效率低?
-
每個服務都是獨立部署,當有某個服務有代碼變更時,只需要打包部署該服務即可
-
-
系統可用性差?
-
每個服務獨立部署,并且做好服務隔離,使用自己的服務器資源,不會影響到其它服務
-
當然,微服務架構在拆分的過程中,還會面臨很多其它問題:
-
如果出現跨服務的業務該如何處理?
-
頁面請求到底該訪問哪個服務?
-
如何實現各個服務之間的服務隔離?
這些問題,我們在下面會給大家逐一解答。
1.3.SpringCloud
微服務拆分以后碰到的各種問題都有對應的解決方案和微服務組件,而SpringCloud框架可以說是目前Java領域最全面的微服務組件的集合了。
而且SpringCloud依托于SpringBoot的自動裝配能力,大大降低了其項目搭建、組件使用的成本。對于沒有自研微服務組件能力的中小型企業,使用SpringCloud全家桶來實現微服務開發可以說是最合適的選擇!
SpringCloud官網https://b11et3un53m.feishu.cn/wiki/R4Sdwvo8Si4kilkSKfscgQX0niB#N8judeJvGors0yxVEU7cCudwnjp
2.微服務拆分
接下來,我們就一起將黑馬商城這個單體項目拆分為微服務項目,并解決其中出現的各種問題。
2.1.熟悉黑馬商城
首先,我們需要熟悉黑馬商城項目的基本結構:
2.1.1.登錄
首先來看一下登錄業務流程:
2.2.2.搜索商品
在首頁搜索框輸入關鍵字,點擊搜索即可進入搜索列表頁面:
2.2.3.購物車
在搜索到的商品列表中,點擊按鈕加入購物車
,即可將商品加入購物車:
2.2.4.下單
在購物車頁面點擊結算
按鈕,會進入訂單結算頁面:
點擊提交訂單,會提交請求到服務端,服務端做3件事情:
-
創建一個新的訂單
-
扣減商品庫存
-
清理購物車中商品
2.2.5.支付
下單完成后會跳轉到支付頁面,目前只支持余額支付:
在選擇余額支付這種方式后,會發起請求到服務端,服務端會立刻創建一個支付流水單,并返回支付流水單號到前端。
當用戶輸入用戶密碼,然后點擊確認支付時,頁面會發送請求到服務端,而服務端會做幾件事情:
-
校驗用戶密碼
-
扣減余額
-
修改支付流水狀態
-
修改交易訂單狀態
2.2.服務拆分原則
服務拆分一定要考慮幾個問題:
-
什么時候拆?
-
如何拆?
2.2.1.什么時候拆
對于多數小型項目,一般是先采用單體架構,隨著用戶規模擴大、業務復雜后再逐漸拆分為微服務架構。問題就是后期服務拆分時,可能會遇到代碼耦合帶來的問題,拆分較為困難(前易后難)
對于一些大型項目,在立項之初目的就很明確,為了長遠考慮,在架構設計時就直接選擇微服務架構。雖然前期投入較多,但后期就少了拆分服務的煩惱(前難后易)
2.2.2.怎么拆
之前我們說過,微服務拆分時粒度要小,這其實是拆分的目標。
高內聚首先是單一職責,同時保證微服務內部業務的完整性為前提,一旦微服務做到了高內聚,那么服務之間的耦合度自然就降低了。
當然,微服務之間不可避免的會有或多或少的業務交互,比如下單時需要查詢商品數據。這個時候我們不能在訂單服務直接查詢商品數據庫,否則就導致了數據耦合。而應該由商品服務對應暴露接口,并且一定要保證微服務對外接口的穩定性(盡量保證接口外觀不變)。雖然出現了服務間調用,但此時無論你如何在商品服務做內部修改,都不會影響到訂單微服務,服務間的耦合度就降低了。
明確了拆分目標,接下來就是拆分方式了。我們在做服務拆分時一般有兩種方式:
-
縱向拆分
-
橫向拆分
所謂縱向拆分,就是按照項目的功能模塊來拆分。例如黑馬商城中,就有用戶管理功能、訂單管理功能、購物車功能、商品管理功能、支付功能等。那么按照功能模塊將他們拆分為一個個服務,就屬于縱向拆分。這種拆分模式可以盡可能提高服務的內聚性。
而橫向拆分,是看各個功能模塊之間有沒有公共的業務部分,如果有將其抽取出來作為通用服務。例如用戶登錄是需要發送消息通知,記錄風控數據,下單時也要發送短信,記錄風控數據。因此消息發送、風控數據記錄就是通用的業務功能,因此可以將他們分別抽取為公共服務:消息中心服務、風控管理服務。這樣可以提高業務的復用性,避免重復開發。同時通用業務一般接口穩定性較強,也不會使服務之間過分耦合。
2.3.拆分購物車、商品服務
一般微服務項目有兩種不同的工程結構:
-
完全解耦:每一個微服務都創建為一個獨立的工程,項目完全解耦。
-
優點:服務之間耦合度低
-
缺點:每個項目都有自己的獨立倉庫,管理麻煩
-
-
Maven聚合:整個項目為一個Project,然后每個微服務是其中的一個Module
-
優點:項目代碼集中,管理和運維方便
-
缺點:服務之間耦合,編譯時間較長
-
2.3.1.商品服務
引入依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>hmall</artifactId><groupId>com.heima</groupId><version>1.0.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>item-service</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--數據庫--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><!--單元測試--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
代碼如下:
package com.hmall.item;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("com.hmall.item.mapper")
@SpringBootApplication
public class ItemApplication {public static void main(String[] args) {SpringApplication.run(ItemApplication.class, args);}
}
需要改變的地方
這也是因為ItemMapper的所在包發生了變化,因此這里代碼必須修改包路徑。
最后,還要導入數據庫表。默認的數據庫連接的是虛擬機,在你docker數據庫執行課前資料提供的SQL文件:(idea自帶的數據庫)
開啟啟動類:
接著,啟動項目,訪問商品微服務的swagger接口文檔:http://localhost:8081/doc.html
然后測試其中的根據id批量查詢商品這個接口:
2.4.服務調用
在拆分的時候,我們發現一個問題:就是購物車業務中需要查詢商品信息,但商品信息查詢的邏輯全部遷移到item-service服務,導致我們無法查詢。
最終結果就是查詢到的購物車數據不完整,因此要想解決這個問題,我們就必須改造其中的代碼,把原本本地方法調用,改造成跨微服務的遠程調用(RPC,即Remote Produce Call)。
因此,現在查詢購物車列表的流程變成了這樣:
那么:我們該如何用Java代碼發送Http的請求呢?
2.4.1.RestTemplate
Spring給我們提供了一個RestTemplate的API,可以方便的實現Http請求的發送。
同步客戶端執行HTTP請求,在底層HTTP客戶端庫(如JDK HttpURLConnection、Apache HttpComponents等)上公開一個簡單的模板方法API。RestTemplate通過HTTP方法為常見場景提供了模板,此外還提供了支持不太常見情況的通用交換和執行方法。 RestTemplate通常用作共享組件。然而,它的配置不支持并發修改,因此它的配置通常是在啟動時準備的。如果需要,您可以在啟動時創建多個不同配置的RestTemplate實例。如果這些實例需要共享HTTP客戶端資源,它們可以使用相同的底層ClientHttpRequestFactory。 注意:從5.0開始,這個類處于維護模式,只有對更改和錯誤的小請求才會被接受。請考慮使用webclient,它有更現代的API,支持同步、異步和流場景。
先將RestTemplate注冊為一個Bean:
package com.hmall.cart.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class RemoteCallConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
重點:
此處不用@Resource和@Autowired,是為了避免循環依賴
故而可以采用構造方法的方式來使用bean對象
但是,這樣代碼非常繁瑣,故而可以使用@AllArgsConstructor來自動生成構造函數
然而,這樣還會出現一個問題,并不是每一個成員變量都需要生成對應的構造方法
因此,可以在需要的成員變量上面添加final關鍵字,同時加上@RequiredArgsConstructor,使其變為常量
2.4.2.遠程調用
完整代碼如下:
private void handleCartItems(List<CartVO> vos) {// TODO 1.獲取商品idSet<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());// 2.查詢商品// List<ItemDTO> items = itemService.queryItemByIds(itemIds);// 2.1.利用RestTemplate發起http請求,得到http的響應ResponseEntity<List<ItemDTO>> response = restTemplate.exchange("http://localhost:8081/items?ids={ids}",HttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ",")));// 2.2.解析響應if(!response.getStatusCode().is2xxSuccessful()){// 查詢失敗,直接結束return;}List<ItemDTO> items = response.getBody();if (CollUtils.isEmpty(items)) {return;}// 3.轉為 id 到 item的mapMap<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.寫入vofor (CartVO v : vos) {ItemDTO item = itemMap.get(v.getItemId());if (item == null) {continue;}v.setNewPrice(item.getPrice());v.setStatus(item.getStatus());v.setStock(item.getStock());}
}
3.服務注冊和發現
3.1.注冊中心原理
在微服務遠程調用的過程中,包括兩個角色:
-
服務提供者:提供接口供其它微服務訪問
-
服務消費者:調用其它微服務提供的接口
在大型微服務項目中,服務提供者的數量會非常多,為了管理這些服務就引入了注冊中心的概念。注冊中心、服務提供者、服務消費者三者間關系如下:
3.2.Nacos注冊中心
目前開源的注冊中心框架有很多,國內比較常見的有:
-
Eureka:Netflix公司出品,目前被集成在SpringCloud當中,一般用于Java應用
-
Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java應用
-
Consul:HashiCorp公司出品,目前集成在SpringCloud中,不限制微服務語言
以上幾種注冊中心都遵循SpringCloud中的API規范,因此在業務開發使用上沒有太大差異。由于Nacos是國內產品,中文文檔比較豐富,而且同時具備配置管理功能(后面會學習),因此在國內使用較多,課堂中我們會Nacos為例來學習。
官方網站如下:
我們基于Docker來部署Nacos的注冊中心,首先我們要準備MySQL數據庫表,用來存儲Nacos的數據。由于是Docker部署,所以大家需要將資料中的SQL文件導入到你Docker中的MySQL容器中:
然后,將課前資料中的nacos
目錄上傳至虛擬機的/root
目錄。
進入root目錄,然后執行下面的docker命令:
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
執行下面代碼
docker logs -f nacos
啟動完成后,訪問下面地址:http://192.168.150.101:8848/nacos/,注意將192.168.150.101
替換為你自己的虛擬機IP地址。
首次訪問會跳轉到登錄頁,賬號密碼都是nacos
3.3.服務注冊
接下來,我們把item-service
注冊到Nacos,步驟如下:
-
引入依賴
-
配置Nacos地址
-
重啟
3.3.1.添加依賴
在item-service
的pom.xml
中添加依賴:
<!--nacos 服務注冊發現-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.3.2.配置Nacos
在item-service
的application.yml
中添加nacos地址配置:
spring:application:name: item-service # 服務名稱cloud:nacos:server-addr: 192.168.150.101:8848 # nacos地址
3.3.3.啟動服務實例
3.4.服務發現
服務的消費者要去nacos訂閱服務,這個過程就是服務發現,步驟如下:
-
引入依賴
-
配置Nacos地址
-
發現并調用服務
3.4.1.引入依賴
服務發現除了要引入nacos依賴以外,由于還需要負載均衡,因此要引入SpringCloud提供的LoadBalancer依賴。
我們在cart-service
中的pom.xml
中添加下面的依賴:
<!--nacos 服務注冊發現-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
可以發現,這里Nacos的依賴于服務注冊時一致,這個依賴中同時包含了服務注冊和發現的功能。因為任何一個微服務都可以調用別人,也可以被別人調用,即可以是調用者,也可以是提供者。
因此,等一會兒cart-service
啟動,同樣會注冊到Nacos
3.4.2.配置Nacos地址
在cart-service
的application.yml
中添加nacos地址配置:
spring:cloud:nacos:server-addr: 192.168.150.101:8848
3.4.3.發現并調用服務
接下來,服務調用者cart-service
就可以去訂閱item-service
服務了。不過item-service有多個實例,而真正發起調用時只需要知道一個實例的地址。
因此,服務調用者必須利用負載均衡的算法,從多個實例中挑選一個去訪問。常見的負載均衡算法有:
-
隨機
-
輪詢
-
IP的hash
-
最近最少訪問
這里我們可以選擇最簡單的隨機負載均衡。
另外,服務發現需要用到一個工具,DiscoveryClient,SpringCloud已經幫我們自動裝配,我們可以直接注入使用:
我們就可以對原來的遠程調用做修改了,之前調用時我們需要寫死服務提供者的IP和端口:
但現在不需要了,我們通過DiscoveryClient發現服務實例列表,然后通過負載均衡算法,選擇一個實例去調用:
4.OpenFeign
在上一章,我們利用Nacos實現了服務的治理,利用RestTemplate實現了服務的遠程調用。但是遠程調用的代碼太復雜了:
而且這種調用方式,與原本的本地方法調用差異太大,編程時的體驗也不統一,一會兒遠程調用,一會兒本地調用。
因此,我們必須想辦法改變遠程調用的開發模式,讓遠程調用像本地方法調用一樣簡單。而這就要用到OpenFeign組件了。
其實遠程調用的關鍵點就在于四個:
-
請求方式
-
請求路徑
-
請求參數
-
返回值類型
所以,OpenFeign就利用SpringMVC的相關注解來聲明上述4個參數,然后基于動態代理幫我們生成遠程調用的代碼,而無需我們手動再編寫,非常方便。
接下來,我們就通過一個快速入門的案例來體驗一下OpenFeign的便捷吧。
4.1.快速入門
我們還是以cart-service中的查詢我的購物車為例。因此下面的操作都是在cart-service中進行。
4.1.1.引入依賴
在cart-service
服務的pom.xml中引入OpenFeign
的依賴和loadBalancer
依賴:
<!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--負載均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
4.1.2.啟用OpenFeign
接下來,我們在cart-service
的CartApplication
啟動類上添加注解,啟動OpenFeign功能:
4.1.3.編寫OpenFeign客戶端
在cart-service
中,定義一個新的接口,編寫Feign客戶端:
其中代碼如下:
package com.hmall.cart.client;@FeignClient("item-service")
public interface ItemClient {@GetMapping("/items")List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
這里只需要聲明接口,無需實現方法。接口中的幾個關鍵信息:
-
@FeignClient("item-service"):聲明服務名稱
-
@GetMapping:聲明請求方式
-
@GetMapping("/items"):聲明請求路徑
-
@RequestParam("ids") Collection<Long> ids:聲明請求參數
-
List<ItemDTO>:返回值類型
有了上述信息,OpenFeign就可以利用動態代理幫我們實現這個方法,并且發送一個GET
請求,攜帶ids為請求參數,并自動將返回值處理為List<ItemDTO>
。
我們只需要直接調用這個方法,即可實現遠程調用了。
4.1.4.使用FeignClient
最后,我們在cart-service
的com.hmall.cart.service.impl.CartServiceImpl
中改造代碼,直接調用ItemClient
的方法:
修改前
修改之后
feign替我們完成了服務拉取、負載均衡、發送http請求的所有工作,是不是看起來優雅多了。
而且,這里我們不再需要RestTemplate了,還省去了RestTemplate的注冊。
4.2.連接池
Feign底層發起http請求,依賴于其它的框架。其底層支持的http客戶端實現包括:
-
HttpURLConnection:默認實現,不支持連接池
-
Apache HttpClient :支持連接池
-
OKHttp:支持連接池
因此我們通常會使用帶有連接池的客戶端來代替默認的HttpURLConnection。比如,我們使用OK Http.
4.2.1.引入依賴
在cart-service
的pom.xml
中引入依賴:
<!--OK http 的依賴 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
4.2.2.開啟連接池
在cart-service
的application.yml
配置文件中開啟Feign的連接池功能:
feign:okhttp:enabled: true # 開啟OKHttp功能
重啟服務,連接池就生效了。
4.3.最佳實踐
4.3.1.思路分析
相信大家都能想到,避免重復編碼的辦法就是抽取。不過這里有兩種抽取思路:
-
思路1:抽取到微服務之外的公共module
-
思路2:每個微服務自己抽取一個module
思路1
優點:項目耦合度大幅度降低
缺點:項目結構變得更加復雜
思路2
方案1抽取相對麻煩,工程結構相對更復雜,但服務之間耦合度降低。
方案2抽取更加簡單,工程結構也比較清晰,但缺點是整個項目耦合度偏高。
4.3.2.抽取Feign客戶端
由于item-service已經創建好,無法繼續拆分,因此這里我們采用方案2
在hmall
下定義一個新的module,命名為hm-api
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.heima</groupId><artifactId>hmall</artifactId><version>1.0.0</version></parent><artifactId>hm-api</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--負載均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.6.6</version><scope>compile</scope></dependency></dependencies>
</project>
4.3.3.掃描包
接下來,我們在cart-service
的pom.xml
中引入hm-api
模塊:
<!--feign模塊--><dependency><groupId>com.heima</groupId><artifactId>hm-api</artifactId><version>1.0.0</version></dependency>
這里因為ItemClient現在定義到了com.hall.api.client包下,而cart-service的啟動類定義在com.hall.cart包下,掃描不到ItemClient,所以報錯了。
4.4.日志配置
OpenFeign只會在FeignClient所在包的日志級別為DEBUG時,才會輸出日志。而且其日志級別有4級:
-
NONE:不記錄任何日志信息,這是默認值。
-
BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
-
HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭信息
-
FULL:記錄所有請求和響應的明細,包括頭信息、請求體、元數據。
Feign默認的日志級別就是NONE,所以默認我們看不到請求日志。
4.4.1.定義日志級別
在hm-api模塊下新建一個配置類,定義Feign的日志級別:
package com.hmall.api.config;import feign.Logger;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.FULL;}
}
4.4.2.配置
接下來,要讓日志級別生效,還需要配置這個類。有兩種方式:
-
局部生效:在某個
FeignClient
中配置,只對當前FeignClient
生效
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
-
全局生效:在
@EnableFeignClients
中配置,針對所有FeignClient
生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)