SpringCloud OpenFeign的使用舉例詳解
1. OpenFeign
1.1 OpenFeign 的介紹
先來(lái)看看咱們之前寫(xiě)的遠(yuǎn)程方法調(diào)用代碼:
@RequestMapping("/ok")
public String ok(@PathParam("content")String content) {
String url = "http://waiter-service/waiter/up/{content}";
String resp = restTemplate.getForObject(url, String.class, content);
return "調(diào)用成功, 已收到 waiter 的響應(yīng): " + resp;
}
雖然說(shuō) RestTemplate 對(duì) http 封裝后,使用起來(lái)還算方便,但是需要拼接 url,如果 url 很復(fù)雜呢?而且代碼可讀性很差,風(fēng)格也不好統(tǒng)一。
在微服務(wù)之間的通信方式通常分為:RPC 和 HTTP,至于 RPC 后期有機(jī)會(huì)在介紹。
在 SpringCloud 中,默認(rèn)使用的是 HTTP 來(lái)進(jìn)行微服務(wù)的通信,最常用的實(shí)現(xiàn)有兩種:
- RestTemplate
- OpenFeign
RestTemplate 咱們已經(jīng)見(jiàn)過(guò)了,接下來(lái)就學(xué)習(xí)下 OpenFeign,這是一種更優(yōu)雅的遠(yuǎn)程方法調(diào)用的形式。
OpenFeign 是一個(gè)聲明式的 Web Service 客戶(hù)端,它讓微服務(wù)之間的調(diào)用變得更簡(jiǎn)單了。類(lèi)似于 Controller 調(diào)用 Service,只需要?jiǎng)?chuàng)建一個(gè)接口,然后添加注解即可以使用 OpenFeign。
本來(lái)是 Feign 是由 Netflix 公司開(kāi)源的組件,后來(lái)呢,在16年7月發(fā)布了最后一個(gè)版本,就將捐給了社區(qū),16年7月,OpenFeign 首個(gè)版本 9.0.0 發(fā)布后,就一直發(fā)布到現(xiàn)在。
SpringCloud 將 Feign 項(xiàng)目繼承到 SpringCloud 的生態(tài)中,但是受到 Feign 更名的影響,所以 SpringCloudFeign 有兩個(gè) starter。
spring-cloud-starter-feign 和 spring-cloud-starter-openfeign
由于 Feign 停止維護(hù),咱們的項(xiàng)目中使用的是后者 OpenFeign。
1.2 快速使用 OpenFeign
在 cook-service 引入 OpenFeign 依賴(lài)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>在 cook-service 啟動(dòng)類(lèi)添加注解:@EnableFeignClients 開(kāi)啟 OpenFeign 功能。
package com.zlcode.cook;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class CookServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CookServiceApplication.class, args);
}
}編寫(xiě) OpenFeign 客戶(hù)端,基于 SpringMVC 注解來(lái)聲明遠(yuǎn)程調(diào)用的信息。
在 cook 目錄下創(chuàng)建 api 目錄,在這個(gè) api 目錄中創(chuàng)建 WaiterApi 接口。
package com.zlcode.cook.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "waiter-service", path = "/waiter")
public interface WaiterApi {
@RequestMapping("/up/{content}")
String up(@PathVariable("content") String content);
}上述 @FeignClient 注解中的 value 表示注冊(cè)的服務(wù)名,用于服務(wù)發(fā)現(xiàn),F(xiàn)eign底層會(huì)使用 Spring Cloud Load Balance 進(jìn)行負(fù)載均衡,如果使用了 Nacos 負(fù)載均衡策略則使用的是 Nacos 的負(fù)載均衡。path 表示統(tǒng)一接口前綴,與咱們的 WaiterController 對(duì)應(yīng)的。
WaiterController:
package com.zlcode.waiter.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {
@RequestMapping("/up/{content}")
public String up(@PathVariable("content") String content) {
log.info("正在執(zhí)行: " + content);
return "執(zhí)行: " + content + "成功!";
}
}修改 CookController 的遠(yuǎn)程方法調(diào)用代碼:
package com.zlcode.cook.controller;
import com.zlcode.cook.api.WaiterApi;
import jakarta.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/cook")
public class CookController {
@Autowired
private WaiterApi waiterApi;
@RequestMapping("/ok")
public String ok(@PathParam("content")String content) {
String resp = waiterApi.up(content);
return "調(diào)用成功, 已收到 waiter 的響應(yīng): " + resp;
}
}重啟 cook-service,再去瀏覽器中訪問(wèn):http://127.0.0.1:8080/cook/up?content=給25桌上紅燒肉

1.3 接口返回的是自定義對(duì)象該怎么辦?
咱們例子中,waiter-service 的 waiter/up/ 接口返回值是 String,每個(gè) Java 項(xiàng)目都可以用 String 這個(gè)對(duì)象,如果新增一個(gè)接口,獲取服務(wù)員信息呢?返回的是一個(gè) WaiterInfo 對(duì)象,那么在 cook-service 調(diào)用方該如何接收呢?
其實(shí)方法很簡(jiǎn)單,咱們先在 WatierController 新增一個(gè) get-info 接口:
package com.zlcode.waiter.controller;
import com.zlcode.waiter.model.WaiterInfo;
import jakarta.websocket.server.PathParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {
@RequestMapping("/up/{content}")
public String up(@PathVariable("content") String content) {
log.info("正在執(zhí)行: " + content);
return "執(zhí)行: " + content + "成功!";
}
@RequestMapping("/get-info")
public WaiterInfo getInfo(String name) {
WaiterInfo waiterInfo = new WaiterInfo();
waiterInfo.setWaiterId(1111);
waiterInfo.setWaiterName(name);
return waiterInfo;
}
}在 cook-service 的 WatierApi 接口中新增遠(yuǎn)程調(diào)用 waiter-service 提供的 get-info 的接口:

此時(shí)發(fā)現(xiàn)咱們的 cook-service 項(xiàng)目中沒(méi)有 WaiterInfo 這個(gè)類(lèi),這也好辦,去 waiter-service 中把 WaiterInfo 類(lèi)復(fù)制到 cook-service 的 model 目錄中。
package com.zlcode.cook.api;
import com.zlcode.cook.model.WaiterInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "waiter-service", path = "/waiter")
public interface WaiterApi {
@RequestMapping("/up/{content}")
String up(@PathVariable("content") String content);
@RequestMapping("/get-info")
WaiterInfo getInfo(@RequestParam("name") String name);
}果然解決了這個(gè)問(wèn)題,再來(lái)到 CookController 中新增接口去遠(yuǎn)程調(diào)用 get-info 方法:
package com.zlcode.cook.controller;
import com.zlcode.cook.api.WaiterApi;
import com.zlcode.cook.model.WaiterInfo;
import jakarta.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/cook")
public class CookController {
@Autowired
private WaiterApi waiterApi;
@RequestMapping("/ok")
public String ok(@PathParam("content")String content) {
String resp = waiterApi.up(content);
return "調(diào)用成功, 已收到 waiter 的響應(yīng): " + resp;
}
@RequestMapping("/get")
public String get(@RequestParam("name")String name) {
WaiterInfo waiterInfo = waiterApi.getInfo(name);
return "調(diào)用成功, 已收到 waiter 的響應(yīng): " + waiterInfo;
}
}重啟 cook-service 和 waiter-service,在瀏覽器中訪問(wèn):http://127.0.0.1:8080/cook/get?name=張三

調(diào)用成功,拿到了 waiter-service get-info 接口返回的 WaiterInfo 對(duì)象。
但是咱們仔細(xì)想想這段代碼,在 cook-service 項(xiàng)目中存在 WaiterInfo 實(shí)體類(lèi),在 waiter-service 項(xiàng)目中也存在 WaiterInfo 實(shí)體類(lèi)。那隨著業(yè)務(wù)的增多,這樣的冗余代碼也會(huì)越來(lái)越多。而且咱們的 WaiterAPI 和 WaiterController 的代碼也十分相似。
所以上述這樣的寫(xiě)法是不行的,不是最佳的寫(xiě)法,更好的辦法是把把 OpenFeign 抽離出來(lái),作為一個(gè)獨(dú)立的模塊,服務(wù)方把提供的 API 都封裝到這個(gè)獨(dú)立的模塊中,供消費(fèi)方使用。
1.4 OpenFeign 的最佳實(shí)踐
官方推薦的方式是繼承,但是企業(yè)中用的不多,咱們此處只介紹抽離的方式,就像 6.3 最后說(shuō)的,把 OpenFeign 提取出來(lái)成為一個(gè)獨(dú)立的模塊即可,這個(gè)模塊由服務(wù)方去提供。
簡(jiǎn)單來(lái)說(shuō),將 OpenFeign 的 Client 抽取為?個(gè)獨(dú)?的模塊,并把涉及到的實(shí)體類(lèi)等都放在這個(gè)模塊中,打成?個(gè) jar。消費(fèi)方只需要依賴(lài)該 jar 包即可。
步驟如下:
創(chuàng)建一個(gè) Model

引入依賴(lài)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>編寫(xiě) API
將 cook-service 的 WaiterApi 和 WatierInfo 直接復(fù)制進(jìn)來(lái):

將這個(gè) waiter-service-api 打包成 jar 包,單擊 maven 選項(xiàng)中的 install:

將 cook-service 的 WaiterApi 和 WatierInfo 刪除掉,然后引入咱們自己打包的 waiter-service-api 依賴(lài):
<dependency>
<groupId>org.example</groupId>
<artifactId>waiter-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>在啟動(dòng)類(lèi)添加掃描路徑:
package com.zlcode.cook;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.zlcode.api")
public class CookServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CookServiceApplication.class, args);
}
}也可以指定需要加載的 Feign客戶(hù)端:
@EnableFeignClients(basePackageClasses = WaiterApi.class) @EnableFeignClients(basePackages = "com.zlcode.api")
接著重新啟動(dòng) cook-service 項(xiàng)目,然后在瀏覽器中訪問(wèn):http://127.0.0.1:8080/cook/get?name=張三

這種更優(yōu)雅的遠(yuǎn)程方法調(diào)用也就成功了!
1.5 部署時(shí)需要注意事項(xiàng)
由于當(dāng)前項(xiàng)目中使用到了自己封裝的 jar 包,但是 maven 打包默認(rèn)會(huì)從中央倉(cāng)庫(kù)中去獲取,但是咱們的 waiter-service-api 是在本地,這就比較麻煩,目前有三種解決方案:
- 將 waiter-service-api jar 包上傳到 maven 中央倉(cāng)庫(kù),但需要注冊(cè)申請(qǐng),比較麻煩。
- 搭建 maven 私服,上傳 jar 包到私服,也是較麻煩的,企業(yè)中比較推薦。
- 從本地讀取 jar 包,這是在個(gè)人項(xiàng)目階段中比較推薦的。只需要在引入本地 jar 包的項(xiàng)目中修改 pom.xml 文件就 ok 了。
觀先獲取 waiter-service-api 執(zhí)行 install 后的本地 jar 包路徑(通過(guò)控制臺(tái)查看):

把之前引入的依賴(lài)替換成本地目錄(記得將 \ 換成 /):
<dependency>
<groupId>org.example</groupId>
<artifactId>waiter-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>
C:/xxx/xxx/.m2/repository/org/example/waiter-service-api/1.0-SNAPSHOT/waiter-service-api-1.0-SNAPSHOT.jar
</systemPath>
</dependency>把 build 配置項(xiàng)換成:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>includeSystemScope 允許包括系統(tǒng)本地的 jar 包。
接下來(lái)就可以進(jìn)行快樂(lè)的部署到 Linux 服務(wù)器啦,剩下部署的操作這里就不再贅述了。
到此這篇關(guān)于SpringCloud OpenFeign的使用的文章就介紹到這了,更多相關(guān)SpringCloud OpenFeign使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)開(kāi)發(fā)之JDBC操作數(shù)據(jù)庫(kù)增刪改查,分頁(yè)查詢(xún)實(shí)例詳解
這篇文章主要介紹了Java基礎(chǔ)開(kāi)發(fā)之JDBC操作數(shù)據(jù)庫(kù)增刪改查,分頁(yè)查詢(xún)實(shí)例詳解,需要的朋友可以參考下2020-02-02
springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看
這篇文章主要介紹了springboot項(xiàng)目mysql-connector-java默認(rèn)版本如何查看問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
基于springboot的flowable工作流實(shí)戰(zhàn)流程分析
這篇文章主要介紹了基于springboot的flowable工作流實(shí)戰(zhàn)流程分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
java使用elasticsearch分組進(jìn)行聚合查詢(xún)過(guò)程解析
這篇文章主要介紹了java使用elasticsearch分組進(jìn)行聚合查詢(xún)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
spring cloud 使用Zuul 實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)問(wèn)題
這篇文章主要介紹了spring cloud 使用Zuul 實(shí)現(xiàn)API網(wǎng)關(guān)服務(wù)問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05
Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼
這篇文章主要介紹了Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java16新特性record類(lèi)使用細(xì)節(jié)示例詳解
這篇文章主要為大家介紹了Java16新特性record類(lèi)使用細(xì)節(jié)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
sharding-jdbc5.0.0實(shí)現(xiàn)分表實(shí)踐
本文主要介紹了sharding-jdbc5.0.0分表實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02

