微服務(wù)實(shí)戰(zhàn):服務(wù)遠(yuǎn)程調(diào)用組件 Spring Cloud Feign 架構(gòu)原理及用法,實(shí)戰(zhàn)講解!
一、背景介紹
今天通過這篇文章,結(jié)合之前的知識(shí),我們一起來了解一下 Spring Cloud 技術(shù)體系中另一個(gè)最核心的組件之一 Fegin。
二、Fegin 簡(jiǎn)介
Spring Cloud Feign 是一套基于 Netflix Feign 實(shí)現(xiàn)的 HTTP 客戶端工具,主要作用是簡(jiǎn)化 HTTP 客戶端的開發(fā)和維護(hù)工作。
傳統(tǒng)的模式下,當(dāng)我們要對(duì)某個(gè)接口發(fā)起 HTTP 請(qǐng)求時(shí),首先會(huì)封裝 HTTP 請(qǐng)求報(bào)文,然后發(fā)起請(qǐng)求,最后處理響應(yīng)結(jié)果。例如之前介紹過的RestTemplate
工具。
其實(shí)這三步驟,可以編寫一個(gè)動(dòng)態(tài)代理類來幫助我們以一種更簡(jiǎn)潔、易于維護(hù)的方式完成 HTTP 請(qǐng)求的調(diào)用。Fegin 的實(shí)現(xiàn)邏輯大體就是這種思路,我們只需要?jiǎng)?chuàng)建一個(gè)接口并添加@FeignClient
注解,然后配置相關(guān)的請(qǐng)求方法既可完成 HTTP 請(qǐng)求工作,剩下的就交給代理類來完成。不過底層,使用的依然是Apache HttpClient
或OkHttp
發(fā)送請(qǐng)求。
與原生 Feign 組件相比,Spring Cloud Feign 還擴(kuò)展了對(duì) Spring MVC 注解的支持,同時(shí)還整合了 Ribbon 提供客戶端的負(fù)載均衡實(shí)現(xiàn),以及 Hystrix 服務(wù)熔斷器。
下面我們通過具體的例子,看看如何使用 Feign 來實(shí)現(xiàn) HTTP 請(qǐng)求。
三、方案實(shí)踐
與之前介紹 Ribbon 類似,依次創(chuàng)建eureka-server
、eureka-provider-1
、eureka-provider-2
工程,就不重復(fù)粘貼了。
根據(jù)eureka-consumer
復(fù)制一個(gè)服務(wù)消費(fèi)者工程,命名為eureka-consumer-feign
,并在pom.xml
中引入 Feign 依賴包,示例如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后,創(chuàng)建一個(gè)服務(wù)啟動(dòng)類并添加@EnableFeignClients
注解,表示開啟掃描 Spring Cloud Feign 客戶端。
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
接著,創(chuàng)建一個(gè)接口并使用@FeignClient
注解指定要調(diào)用的目標(biāo)服務(wù)實(shí)例名稱,接口中定義的各個(gè)方法使用 Spring MVC 的注解就可以指定要調(diào)用的目標(biāo)服務(wù)接口地址,示例如下:
/**
* 配置要調(diào)用的服務(wù)實(shí)例名稱
*/
@FeignClient(name = "eureka-provider")
public interface RpcService {
/**
* 要調(diào)用的目標(biāo)服務(wù)接口地址
* @return
*/
@RequestMapping(value = "/hello")
String hello();
}
最后,創(chuàng)建一個(gè)controller
,通過定義的 feign 客戶端來調(diào)用服務(wù)提供方的接口。
@RestController
public class HelloController {
@Autowired
private RpcService rpcService;
/**
* 發(fā)起遠(yuǎn)程調(diào)用測(cè)試
* @return
*/
@GetMapping("/rpc")
public String rpc() {
String result = rpcService.hello();
return "發(fā)起遠(yuǎn)程調(diào)用,收到返回的信息:" + result;
}
}
完成以上工程之后,依次將eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服務(wù)啟動(dòng)起來。
然后在瀏覽器上多次訪問http://localhost:9003/rpc
,可以得到類似于如下內(nèi)容。
圖片
可以清晰的看到,客戶端以輪訓(xùn)的方式調(diào)用目標(biāo)接口。至此,最簡(jiǎn)單的一個(gè)服務(wù)注冊(cè)與調(diào)用的例子就完成了。
四、Fegin 傳輸文件
默認(rèn)情況下,F(xiàn)egin 可以滿足絕大部分的 HTTP 請(qǐng)求場(chǎng)景。
但是在某些場(chǎng)景下,比如在服務(wù)之間實(shí)現(xiàn)文件遠(yuǎn)程上傳,如何實(shí)現(xiàn)呢?
實(shí)際上,Spring Cloud Feign 并不支持直接傳文件,但可以通過引入 Feign 的擴(kuò)展包來實(shí)現(xiàn)。
具體實(shí)現(xiàn)例子如下。
4.1服務(wù)提供方(接收文件)
服務(wù)提供方的實(shí)現(xiàn)比較簡(jiǎn)單,按照 Spring MVC 的正常實(shí)現(xiàn)即可,文件上傳接口示例如下:
@RestController
publicclass HelloController {
privatestaticfinal String SRC_PATH = "/Users/demo/file/";
@PostMapping("/fileUpload")
public String fileUpload(@RequestParam("file") MultipartFile file,
@RequestParam("prefixName") String prefixName) throws IOException {
// 獲取上傳文件的文件名
String fileName = file.getOriginalFilename();
String absolutePath = SRC_PATH + prefixName + "_" + fileName;
// 將文件保存到磁盤
file.transferTo(new File(absolutePath));
return"Upload file success:" + prefixName + "_" + fileName;
}
}
4.2服務(wù)消費(fèi)方(發(fā)送文件)
在服務(wù)消費(fèi)方,由于需要利用 Feign 客戶端來上傳文件,需要在pom.xml
文件引入支持文件上傳的依賴包,內(nèi)容如下。
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.0.3</version>
</dependency>
接著,定義一個(gè)文件上傳的 Feign 客戶端接口,示例如下。
@FeignClient(name = "eureka-provider", configuration = FeignSupportConfig.class)
public interface RpcUploadService {
@PostMapping(value = "/fileUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String handleFileUpload(@RequestPart(value = "file") MultipartFile file,
@RequestParam("prefixName") String prefixName);
}
然后,為@FeignClient
注解類創(chuàng)建所需的編碼器,也就是上文所配置的FeignSupportConfig
類,不然調(diào)用的時(shí)候會(huì)報(bào)錯(cuò)。
@Configuration
public class FeignSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
/**
* 微服務(wù)傳輸文件用
* @return
*/
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
最后,創(chuàng)建一個(gè)controller
,通過上文定義的 feign 客戶端來上傳文件到服務(wù)端。
@RestController
public class HelloController {
@Autowired
private RpcUploadService rpcUploadService;
@PostMapping("/rpcUpload")
public String rpcUpload(@RequestParam("file") MultipartFile file) throws IOException {
String result = rpcUploadService.handleFileUpload(file,"feign");
return "通過 feign 發(fā)起文件遠(yuǎn)程上傳調(diào)用,收到返回的信息:" + result;
}
}
完成以上操作之后,依次將eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服務(wù)啟動(dòng)起來。
用 postman 調(diào)用客戶端接口上傳文件,不出意外的話,會(huì)看到類似如下的返回信息。
圖片
可以清晰的看到,文件遠(yuǎn)程上傳成功。
五、小結(jié)
最后總結(jié)一下,F(xiàn)eign 是一個(gè)輕量級(jí)的 HTTP 客戶端框架,使用者能夠以一種更簡(jiǎn)潔、易于維護(hù)的方式來實(shí)現(xiàn) HTTP 服務(wù)請(qǐng)求。同時(shí)在 Spring Cloud 生態(tài)中,F(xiàn)eign 整合了 Ribbon,可以自動(dòng)實(shí)現(xiàn)客戶端負(fù)載均衡功能。
另外,F(xiàn)eign 還整合了 Hystrix 來實(shí)現(xiàn)服務(wù)的容錯(cuò)保護(hù),在下一篇文章中我們會(huì)對(duì)其進(jìn)行介紹。