微服務實戰:服務遠程調用組件 Spring Cloud Feign 架構原理及用法,實戰講解!
一、背景介紹
今天通過這篇文章,結合之前的知識,我們一起來了解一下 Spring Cloud 技術體系中另一個最核心的組件之一 Fegin。
二、Fegin 簡介
Spring Cloud Feign 是一套基于 Netflix Feign 實現的 HTTP 客戶端工具,主要作用是簡化 HTTP 客戶端的開發和維護工作。
傳統的模式下,當我們要對某個接口發起 HTTP 請求時,首先會封裝 HTTP 請求報文,然后發起請求,最后處理響應結果。例如之前介紹過的RestTemplate
工具。
其實這三步驟,可以編寫一個動態代理類來幫助我們以一種更簡潔、易于維護的方式完成 HTTP 請求的調用。Fegin 的實現邏輯大體就是這種思路,我們只需要創建一個接口并添加@FeignClient
注解,然后配置相關的請求方法既可完成 HTTP 請求工作,剩下的就交給代理類來完成。不過底層,使用的依然是Apache HttpClient
或OkHttp
發送請求。
與原生 Feign 組件相比,Spring Cloud Feign 還擴展了對 Spring MVC 注解的支持,同時還整合了 Ribbon 提供客戶端的負載均衡實現,以及 Hystrix 服務熔斷器。
下面我們通過具體的例子,看看如何使用 Feign 來實現 HTTP 請求。
三、方案實踐
與之前介紹 Ribbon 類似,依次創建eureka-server
、eureka-provider-1
、eureka-provider-2
工程,就不重復粘貼了。
根據eureka-consumer
復制一個服務消費者工程,命名為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>
然后,創建一個服務啟動類并添加@EnableFeignClients
注解,表示開啟掃描 Spring Cloud Feign 客戶端。
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
接著,創建一個接口并使用@FeignClient
注解指定要調用的目標服務實例名稱,接口中定義的各個方法使用 Spring MVC 的注解就可以指定要調用的目標服務接口地址,示例如下:
/**
* 配置要調用的服務實例名稱
*/
@FeignClient(name = "eureka-provider")
public interface RpcService {
/**
* 要調用的目標服務接口地址
* @return
*/
@RequestMapping(value = "/hello")
String hello();
}
最后,創建一個controller
,通過定義的 feign 客戶端來調用服務提供方的接口。
@RestController
public class HelloController {
@Autowired
private RpcService rpcService;
/**
* 發起遠程調用測試
* @return
*/
@GetMapping("/rpc")
public String rpc() {
String result = rpcService.hello();
return "發起遠程調用,收到返回的信息:" + result;
}
}
完成以上工程之后,依次將eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服務啟動起來。
然后在瀏覽器上多次訪問http://localhost:9003/rpc
,可以得到類似于如下內容。
圖片
可以清晰的看到,客戶端以輪訓的方式調用目標接口。至此,最簡單的一個服務注冊與調用的例子就完成了。
四、Fegin 傳輸文件
默認情況下,Fegin 可以滿足絕大部分的 HTTP 請求場景。
但是在某些場景下,比如在服務之間實現文件遠程上傳,如何實現呢?
實際上,Spring Cloud Feign 并不支持直接傳文件,但可以通過引入 Feign 的擴展包來實現。
具體實現例子如下。
4.1服務提供方(接收文件)
服務提供方的實現比較簡單,按照 Spring MVC 的正常實現即可,文件上傳接口示例如下:
@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服務消費方(發送文件)
在服務消費方,由于需要利用 Feign 客戶端來上傳文件,需要在pom.xml
文件引入支持文件上傳的依賴包,內容如下。
<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>
接著,定義一個文件上傳的 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
注解類創建所需的編碼器,也就是上文所配置的FeignSupportConfig
類,不然調用的時候會報錯。
@Configuration
public class FeignSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
/**
* 微服務傳輸文件用
* @return
*/
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
最后,創建一個controller
,通過上文定義的 feign 客戶端來上傳文件到服務端。
@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 發起文件遠程上傳調用,收到返回的信息:" + result;
}
}
完成以上操作之后,依次將eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服務啟動起來。
用 postman 調用客戶端接口上傳文件,不出意外的話,會看到類似如下的返回信息。
圖片
可以清晰的看到,文件遠程上傳成功。
五、小結
最后總結一下,Feign 是一個輕量級的 HTTP 客戶端框架,使用者能夠以一種更簡潔、易于維護的方式來實現 HTTP 服務請求。同時在 Spring Cloud 生態中,Feign 整合了 Ribbon,可以自動實現客戶端負載均衡功能。
另外,Feign 還整合了 Hystrix 來實現服務的容錯保護,在下一篇文章中我們會對其進行介紹。