最近剛好要和另一個服務進行頻繁的遠程調用,即使對restTemplate也有所封裝,但還是厭倦了restTemplate發送時的各種參數拼接,於是決定嘗試下openFeign;
feign是一個聲明式的Web服務客戶端,它使得發送web請求變得很容易,而openFign是springcloud對feign的一個升級,可以支持springMvc的註解;
接下來描述下我是怎麼使用openFeign發送web請求的,首先需要明確,使用openFeign是有一定的代碼侵入的,不過侵入的是consumer的代碼,通過在consumer中添加openFeign的註解來拼裝請求併發送,
一般有兩種方式,一種是consumer端根據provider提供的接口文檔編寫調用用例.另一種是由Prodiver提供一個接口API的jar包供consumer調用;下面上我的測試步驟及代碼,兩種方式都有涉及;
consumer端
首先在consumer中引入openFeigin以及HTTPClient的依賴:
<!-- 引入open-feign的依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 引入open-feign的httpClien依賴 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency> <!-- 引入Prodiver提供的接口包(根據實際需要) --> <dependency> <groupId>com.darling.api</groupId> <artifactId>server-apis</artifactId> <version>0.0.1-SNAPSHOT</version> <scope>compile</scope> </dependency>
然後在consumer中封裝一個service引入feign,來進行遠程調用,關鍵代碼如下:
package com.darling.eureka.consumer.service; import com.darling.api.service.UserService; import com.darling.eureka.consumer.model.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; /** * @description: 通過openFeign遠程調用服務提供者 * @author: dll * @date: Created in 2021/9/14 12:29 * @version: 1.0 * @modified By: */ @FeignClient(name = "EUREKA-PROVIDER") public interface UserApiService extends UserService{ @GetMapping("/test/sayHi?name={name}") String sayHi(@PathVariable String name); /** * 測試 插入一條信息 * @param user * @return */ @GetMapping("/test/insertInfo") String insertInfo(@RequestBody User user); }
說明:
1、consumer中的啓動類需要加上@EnableFeignClients註解,這裏代碼沒有貼出
2、FeignClient註解有多種屬性可選,如自定義配置信息的configuration、直接指定訪問鏈接的url等,由於我這裏只是一個測試的demo就直接用name來指定在eureka中註冊的Prodiver的服務名稱了;
3、代碼中的GetMapping就是上面說的兩種方式之一,consumer根據Prodiver提供的接口文檔進行編碼調用的;
4、代碼中的UserService就是上面說的另一種方式,由服務提供者提供的一個接口依賴包,我這裏包名叫server-apis,,這樣consumer中就可以像調用本地方法一樣調用遠程服務提供的接口能力,下面貼上UserService的代碼:
package com.darling.api.service; import com.darling.api.model.UserInfo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; /** * @description: * @author: dll * @date: Created in 2021/9/15 22:02 * @version: * @modified By: */ @RequestMapping("/serverApis") public interface UserService { @GetMapping("/test") UserInfo test(); /** * 獲取用戶信息 * @return 用戶姓名、年齡等信息 */ @GetMapping("/getInfo") UserInfo getInfo(); }
需要注意的是,無論是通過Prodiver提供的依賴包還是直接編寫調用代碼,以上代碼中所有的類似GetMapping、PostMapping的註解都是由Prodiver暴露出來的接口請求路徑,由feign負責識別、拼裝併發起遠程請求的;
下面來看看consumer中的Controller的調用代碼:
package com.darling.eureka.consumer.controller; import com.darling.api.model.UserInfo; import com.darling.eureka.consumer.model.User; import com.darling.eureka.consumer.service.UserApiService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @description: 測試openFeign的遠程調用 * @author: dll * @date: Created in 2021/8/25 21:52 * @version: 1.0 * @modified By: */ @RestController @RequestMapping("/consumer/test") public class TestConsumerController { @Resource private UserApiService userApiService; /** * 調用UserApiService中自己根據接口文檔編寫的調用代碼 * @return */ @RequestMapping("/sayHello") public String sayHello() { String res = userApiService.sayHi("hahaha"); System.out.println("res = " + res); return res; } /** * 調用UserApiService中自己根據接口文檔編寫的調用代碼 * @param user * @return */ @RequestMapping("/insert") public String insertInfo(User user) { String res = userApiService.insertInfo(user); System.out.println("res = " + res); return res; } /** * 調用UserApiService中由Prodiver提供的server-apis依賴包中的UserService的接口 * @return */ @RequestMapping("/getUserInfo") public UserInfo getUserInfo() { UserInfo res = userApiService.getInfo(); System.out.println("res = " + res); return res; } }
provider端
如果沒有采用由Prodiver提供接口依賴包的方式的話,使用openFeign對Prodiver端幾乎無任何侵入,Prodiver只需寫好自己可以提供的服務並提供一份接口文檔即可;
下面主要聊聊如果通過提供接口依賴包的方式Prodiver應該做哪些調整以及其中可能遇到的坑
首先,需要定義一個專門用來提供接口能力的api服務(server-apis),就正常的web服務即可,代碼可參見上面的UserService;然後由Prodiver引入這個服務;需要注意的是如果通過這種方式提供服務的話,Prodiver
需要以接口實現類的方式來提供具體服務的實現,不能在controller中定義接口的調用路徑了,否則訪問會直接404;
總結
通過以上的調用實例,我們可以得出結論,feign調用雖香但是對代碼的確會有一定的侵入性,主要針對服務的調用者而言,而是通過依賴包調用還是由consumer自己編碼調用,無非是願意接受jar包耦合還是接口文檔耦合了,
所以對於同一個團隊開發的consumer和Prodiver來說,個人更傾向於通過定義統一的接口依賴包,雙方嚴格按照依賴包進行開發,依賴包的變更大家都能實時感知並作出及時響應,調用起來也更方便;而對於跨團隊來開發consumer
和Prodiver來說,個人認爲通過接口文檔會更合理,consumer相對來說能把命運掌握在自己手上,對接的時候只認接口文檔,減少了對接口包的依賴