Spring?Cloud實(shí)現(xiàn)遠(yuǎn)程調(diào)用OpenFeign組件的方法
一、RestTemplate存在問(wèn)題
觀察之前文章介紹的遠(yuǎn)程調(diào)用的代碼:
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://product-service/product/"+ orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url,
ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
雖說(shuō)RestTemplate 對(duì)HTTP封裝后, 已經(jīng)比直接使用HTTPClient簡(jiǎn)單方便很多, 但是還存在一些問(wèn)題.
- 需要拼接URL, 靈活性高, 但是封裝臃腫, URL復(fù)雜時(shí), 容易出錯(cuò).
- 代碼可讀性差, 風(fēng)格不統(tǒng)?.
微服務(wù)之間的通信方式, 通常有兩種: RPC 和 HTTP.
在SpringCloud中, 默認(rèn)是使用HTTP來(lái)進(jìn)行微服務(wù)的通信, 最常用的實(shí)現(xiàn)形式有兩種:
• RestTemplate
• OpenFeign
RPC(Remote Procedure Call)遠(yuǎn)程過(guò)程調(diào)用,是?種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)上請(qǐng)求服務(wù),而不需
要了解底層網(wǎng)絡(luò)通信細(xì)節(jié)。RPC可以使用多種網(wǎng)絡(luò)協(xié)議進(jìn)行通信, 如HTTP、TCP、UDP等, 并且在
TCP/IP網(wǎng)絡(luò)四層模型中跨越了傳輸層和應(yīng)用層。簡(jiǎn)言之RPC就是像調(diào)用本地方法?樣調(diào)用遠(yuǎn)程方法。
常見(jiàn)的RPC框架有:
1 . Dubbo: Apache Dubbo 中文
2 . Thrift : Apache Thrift - Home
3 . gRPC: gRPC
二、OpenFeign介紹
OpenFeign 是一個(gè)聲明式的 Web Service 客戶端. 它讓微服務(wù)之間的調(diào)用變得更簡(jiǎn)單, 類似controller調(diào)用service, 只需要?jiǎng)?chuàng)建?個(gè)接口,然后添加注解即可使用OpenFeign.
OpenFeign 的前身
Feign 是 Netflix 公司開(kāi)源的?個(gè)組件.
• 2013年6月, Netflix發(fā)布 Feign的第?個(gè)版本 1.0.0
• 2016年7月, Netflix發(fā)布Feign的最后?個(gè)版本 8.18.0
2016年,Netflix 將 Feign 捐獻(xiàn)給社區(qū)
• 2016年7月 OpenFeign 的首個(gè)版本 9.0.0 發(fā)布,之后?直持續(xù)發(fā)布到現(xiàn)在.
Spring Cloud Feign
Spring Cloud Feign 是 Spring 對(duì) Feign 的封裝, 將 Feign 項(xiàng)目集成到 Spring Cloud ?態(tài)系統(tǒng)中.受 Feign 更名影響,Spring Cloud Feign 也有兩個(gè) starter
spring-cloud-starter-feign
spring-cloud-starter-openfeign
由于Feign的停更維護(hù), 對(duì)應(yīng)的, 我們使用的依賴是 spring-cloud-starter-openfeign。
OpenFeign :官方文檔
Spring Cloud Feign:官方文檔
三、快速上手
3.1 引入依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
3.2 添加注解
在order-service的啟動(dòng)類添加注解 @EnableFeignClients , 開(kāi)啟OpenFeign的功能.
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
3.3 編寫(xiě)OpenFeign的客戶端
基于SpringMVC的注解來(lái)聲明遠(yuǎn)程調(diào)用的信息
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
@FeignClient 注解作用在接口上, 參數(shù)說(shuō)明:
• name/value:指定FeignClient的名稱, 也就是微服務(wù)的名稱, 用于服務(wù)發(fā)現(xiàn), Feign底層會(huì)使用Spring Cloud LoadBalance進(jìn)行負(fù)載均衡. 也可以使用 url 屬性指定?個(gè)具體的url.
• path: 定義當(dāng)前FeignClient的統(tǒng)一前綴.
3.4 遠(yuǎn)程調(diào)用
修改遠(yuǎn)程調(diào)用的方法
@Autowired
private ProductApi productApi;
/**
* Feign實(shí)現(xiàn)遠(yuǎn)程調(diào)?
* @param orderId
*/
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
ProductInfo productInfo =
productApi.getProductById(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
不同方式之間代碼對(duì)比:

四、OpenFeign參數(shù)傳遞
通過(guò)觀察, 我們也可以發(fā)現(xiàn), Feign的客戶端和服務(wù)提供者的接口聲明非常相似
上面例子中, 介紹了Feign 從URL中獲取參數(shù), 接下來(lái)介紹下Feign參數(shù)傳遞的其他方式。
4.1 傳遞單個(gè)參數(shù)
服務(wù)提供方:product-service
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id){
return "p1接收到參數(shù):"+id;
}
}
Feign客戶端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
}
服務(wù)消費(fèi)方order-service:
@RequestMapping("/feign")
@RestController
public class TestFeignController {
@Autowired
private ProductApi productApi;
@RequestMapping("/o1")
public String o1(Integer id){
return productApi.p1(id);
}
}
測(cè)試遠(yuǎn)程調(diào)用:
http://127.0.0.1:8080/feign/o1?id=5
測(cè)試結(jié)果:

4.2 傳遞多個(gè)參數(shù)
使用多個(gè)@RequestParam 進(jìn)行參數(shù)綁定即可
服務(wù)提供方 product-service
@RequestMapping("/p2")
public String p2(Integer id,String name){
return "p2接收到參數(shù),id:"+id +",name:"+name;
}
Feign客戶端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p2")
String p2(@RequestParam("id")Integer id,@RequestParam("name")String name);
}
服務(wù)消費(fèi)方order-service:
@RequestMapping("/o2")
public String o2(@RequestParam("id")Integer id,@RequestParam("name")String name){
return productApi.p2(id,name);
}
測(cè)試遠(yuǎn)程調(diào)用:
http://127.0.0.1:8080/feign/o2?id=5&name=zhangsan

4.3 傳遞對(duì)象
服務(wù)提供方 product-service
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "接收到對(duì)象, productInfo:"+productInfo;
}
Feign客戶端:
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
服務(wù)消費(fèi)方order-service:
@RequestMapping("/o3")
public String o3(ProductInfo productInfo){
return productApi.p3(productInfo);
}
4.4 傳遞JSON
服務(wù)提供方 product-service:
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "接收到對(duì)象, productInfo:"+productInfo;
}
Feign客戶端
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
服務(wù)消費(fèi)方order-service:
@RequestMapping("/o4")
public String o4(@RequestBody ProductInfo productInfo){
System.out.println(productInfo.toString());
return productApi.p4(productInfo);
}
五、最佳實(shí)踐
最佳實(shí)踐, 其實(shí)也就是經(jīng)過(guò)歷史的迭代, 在項(xiàng)目中的實(shí)踐過(guò)程中, 總結(jié)出來(lái)的最好的使用方式.
通過(guò)觀察, 我們也能看出來(lái), Feign的客戶端與服務(wù)提供者的controller代碼非常相似
Feign 客戶端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
服務(wù)提供方Controller:
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId){
//...
}
}
有沒(méi)有?種方法可以簡(jiǎn)化這種寫(xiě)法呢?
5.1 Feign繼承方式
Feign 支持繼承的方式, 我們可以把?些常件的操作封裝到接口里.
我們可以定義好?個(gè)接口, 服務(wù)提供方實(shí)現(xiàn)這個(gè)接口, 服務(wù)消費(fèi)方編寫(xiě)Feign 接口的時(shí)候, 直接繼承這個(gè)接口
5.1.1 創(chuàng)建一個(gè)Module
接口可以放在?個(gè)公共的Jar包里, 供服務(wù)提供方和服務(wù)消費(fèi)方使用

5.1.2 引入依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
5.1.3 編寫(xiě)接口
復(fù)制 ProductApi, ProductInfo 到product-api模塊中
public interface ProductInterface {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p2")
String p2(@RequestParam("id")Integer id,@RequestParam("name")String name);
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
5.1.4 服務(wù)提供方
服務(wù)提供方實(shí)現(xiàn)接口ProductInterface:
@RequestMapping("/product")
@RestController
public class ProductController implements ProductInterface {
@Autowired
private ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer
productId){
System.out.println("收到請(qǐng)求,Id:"+productId);
return productService.selectProductById(productId);
}
@RequestMapping("/p1")
public String p1(Integer id){
return "p1接收到參數(shù):"+id;
}
@RequestMapping("/p2")
public String p2(Integer id,String name){
return "p2接收到參數(shù),id:"+id +",name:"+name;
}
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "接收到對(duì)象, productInfo:"+productInfo;
}
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "接收到對(duì)象, productInfo:"+productInfo;
}
}
5.1.5 服務(wù)消費(fèi)方
服務(wù)消費(fèi)方繼承ProductInterface
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi extends ProductInterface {
}
這樣通過(guò)繼承就可以實(shí)現(xiàn)遠(yuǎn)程調(diào)用
5.2 Feign 抽取方式
官方推薦Feign的使用方式為繼承的方式, 但是企業(yè)開(kāi)發(fā)中, 更多是把Feign接口抽取為?個(gè)獨(dú)立的模塊(做法和繼承相似, 但理念不同).
操作方法:
將Feign的Client抽取為?個(gè)獨(dú)立的模塊, 并把涉及到的實(shí)體類等都放在這個(gè)模塊中, 打成?個(gè)Jar. 服務(wù)消費(fèi)方只需要依賴該Jar包即可. 這種方式在企業(yè)中比較常見(jiàn), Jar包通常由服務(wù)提供方來(lái)實(shí)現(xiàn).
5.2.1 創(chuàng)建新module

5.2.2 引入依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
5.2.3 編寫(xiě)API
復(fù)制 ProductApi, ProductInfo 到product-api模塊中
之后通過(guò)Maven打包,可以觀察Maven本地倉(cāng)庫(kù)檢查是否打包成功
5.2.4 服務(wù)消費(fèi)方使用product-api
1 刪除 ProductApi, ProductInfo.
2. 引入依賴:
<dependency> <groupId>org.example</groupId> <artifactId>product-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
修改項(xiàng)目中ProductApi, ProductInfo的路徑為product-api中的路徑
3 .指定掃描類: ProductApi
在啟動(dòng)類添加掃描路徑
@EnableFeignClients(basePackages = {"com.bite.api"})
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
也可以指定需要加載的Feign客戶端
@EnableFeignClients(clients = {ProductApi.class})
六、總結(jié)
以上就是本文全部?jī)?nèi)容,本文主要為大家介紹了Spring Cloud中實(shí)現(xiàn)遠(yuǎn)程調(diào)用的另一個(gè)組件-OpenFeign的相關(guān)知識(shí)與操作。感謝各位能夠看到最后,如有問(wèn)題,歡迎各位大佬在評(píng)論區(qū)指正,希望大家可以有所收獲!創(chuàng)作不易,希望大家多多支持。
到此這篇關(guān)于Spring Cloud實(shí)現(xiàn)遠(yuǎn)程調(diào)用OpenFeign組件的方法的文章就介紹到這了,更多相關(guān)Spring Cloud調(diào)用OpenFeign內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Cloud OpenFeign實(shí)現(xiàn)動(dòng)態(tài)服務(wù)名調(diào)用的示例代碼
- SpringCloudAlibaba微服務(wù)調(diào)用組件OpenFeign的方法
- SpringCloud中的OpenFeign調(diào)用解讀
- Spring?Cloud?OpenFeign?遠(yuǎn)程調(diào)用
- SpringCloud?OpenFeign?服務(wù)調(diào)用傳遞?token的場(chǎng)景分析
- Spring Cloud詳解實(shí)現(xiàn)聲明式微服務(wù)調(diào)用OpenFeign方法
- SpringCloud學(xué)習(xí)筆記之OpenFeign進(jìn)行服務(wù)調(diào)用
- Spring Cloud 系列之服務(wù)調(diào)用 OpenFeign的實(shí)現(xiàn)
- SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法
相關(guān)文章
Java反射機(jī)制之如何獲取對(duì)象動(dòng)態(tài)字段值
文章介紹了Java反射機(jī)制,可動(dòng)態(tài)獲取對(duì)象字段值(包括私有及父類字段),通過(guò)遍歷類結(jié)構(gòu)、設(shè)置訪問(wèn)權(quán)限和遞歸查找實(shí)現(xiàn),適用于復(fù)雜繼承場(chǎng)景和JSON映射,但需注意性能與安全風(fēng)險(xiǎn)2025-07-07
MyBatis-Plus多數(shù)據(jù)源的示例代碼
本文主要介紹了MyBatis-Plus多數(shù)據(jù)源的示例代碼,包括依賴配置、數(shù)據(jù)源配置、Mapper 和 Service 的定義,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
基于servlet的執(zhí)行原理與生命周期(全面解析)
下面小編就為大家分享一篇servlet的執(zhí)行原理與生命周期全面解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
淺談Spring Data如何簡(jiǎn)化數(shù)據(jù)操作的方法
這篇文章主要介紹了看Spring Data如何簡(jiǎn)化數(shù)據(jù)操作的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04

