Spring Cloud Netflix Feign 基礎應用實戰

點擊上方"程序員歷小冰",選擇“置頂或者星標”

   你的關注意義重大!

本文摘自筆者出版的書籍《Spring Cloud 微服務架構進階》

微服務是軟件系統架構上的一種設計風格,它倡導將一個原本獨立的服務系統分成多個小型服務,這些小型服務都在獨立的進程中運行,通過各個小型服務之間的協作來實現原本獨立系統的所有業務功能。小型服務基於多種跨進程的方式進行通信協作,而在 SpringCloud架構中比較常見的跨進程的方式是RESTful HTTP請求和RPC調用。

RPC就是遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。比如說,計算機 A 上的進程調用另外一臺計算機 B 上的進程,其中 A 上的調用進程被掛起,而 B 上的被調用進程開始執行,當值返回給 A 時,A 進程繼續執行。調用方可以通過使用參數將信息傳送給被調用方,而後可以通過傳回的結果得到信息。而這一過程,對於開發人員來說是透明的。

640?wx_fmt=png

REST是Representational State Transfer的縮寫,是表現層狀態轉移的含義。

Resource是資源,所謂“資源”就是網絡上的一個實體,或者說網上的一個具體信息。它可以是一段文本,一首歌曲,一種服務,總之就是一個具體的存在。你可以使用一個URI指向它,每種”資源“對應一個URI。

Representational是”表現層“的意思,”資源“是一種消息實體,它可以有多種外在的表現形式,我們把”資源“的具體呈現出來的形式叫做它的”表現層“。比如說,文本可以用txt格式進行表現,也可以使用xml格式,JSON格式和二進制格式;視頻可以以MP4格式表現,也可以以AVI格式表現。URI只代表資源的實體,不代表它的形式。它的具體表現形式,應該在HTTP請求的頭信息Accept和Content-Type字段指定,這兩個字段纔是對”表現層“的描述。

State Transfer是指狀態轉化。客戶端訪問服務的過程中必然涉及到數據和狀態的轉化。如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生”狀態轉化“。而這種轉化是建立在表現層之上的,所以就是”表現層狀態轉化“。客戶端通過使用HTTP協議中的四個動詞來實現上述操作,它們分別:用來獲取資源的GET,用來新建或更新資源的POST,用來更新資源的PUT,用來刪除資源的DELETE。

REST是Web Service的一種實現方式,另外一種實現方式爲SOAP。REST致力於通過HTTP協議中的POST/GET/PUT/DELETE等方法和一個可讀性較強的URL來提供一個HTTP請求;而SOAP致力於通過wsdl數據格式來實現通信。二者的使用場景和設計目標不同。SOAP一般作爲應用層協議來進行服務間的消息調用。

RPC和REST之間的最大差別在於RPC調用可以不依賴HTTP協議,底層直接使用TPC/IP協議進行傳輸,傳輸效率相比於REST會有一定的提升。

Feign簡介

Feign是一個聲明式RESTful HTTP請求客戶端,它使得編寫Web服務客戶端更加方便和快捷。使用Feign創建一個接口並使用Feign提供的註解修飾該接口,然後就可以使用該接口進行RESTful HTTP請求的發送。Feign還可以集成Ribbon和Eureka來爲自己提供負載均衡和斷路器的機制。

Feign會將帶有註解的函數接口信息轉化爲網絡請求模板,在發送網絡請求之前,函數的參數值會以一定的方式設置到這些請求模板中。雖然這樣的模式使得 Feign只能支持基於文本的網絡請求,但是它可以簡化網絡請求的實現,方便編程人員快速構建自己的網絡請求架構。640?wx_fmt=png

如上圖所示,使用 Feign的程序的架構一般分爲三個部分,分別爲服務註冊中心,服務提供者和服務消費者。服務提供者向服務註冊中心註冊自己,然後服務消費者通過 Feign發送請求時, Feign會向去服務註冊中心獲取關於服務提供者的信息,然後再向服務提供者發送網絡請求。

代碼示例

服務註冊中心

Feign可以配合 eureka等服務註冊中心同時使用。eureka來作爲服務註冊中心,爲 Feign提供關於服務端信息的獲取,比如說IP地址。關於 eureka的具體使用可以參考第四章中關於 eureka的快速入門介紹。

服務提供者

SpringCloudFeign是聲明式RESTful請求客戶端,所以它不會侵入服務提供者程序的實現。也就是說,服務提供者只需要提供Web Service的API接口,至於具體實現既可以是 SpringControler也可以是 Jersey。我們只需要確保該服務提供者被註冊到服務註冊中心上。

@RestController	
@RequestMapping("/feign-service")	
public class FeignServiceController {	
    private static final Logger logger = LoggerFactory.getLogger(FeignServiceController.class);	
    private static String DEFAULT_SERVICE_ID = "application";	
    private static String DEFAULT_HOST = "localhost";	
    private static int DEFAULT_PORT = 8080;	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.GET)	
    public Instance getInstanceByServiceId(@PathVariable("serviceId") String serviceId){	
        logger.info("Get Instance by serviceId {}", serviceId);	
        return new Instance(serviceId, DEFAULT_HOST, DEFAULT_PORT);	
    }	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.DELETE)	
    public String deleteInstanceByServiceId(@PathVariable("serviceId") String serviceId){	
        logger.info("Delete Instance by serviceId {}", serviceId);	
        return "Instance whose serviceId is " + serviceId + " is deleted";	
    }	
    @RequestMapping(value = "/instance", method = RequestMethod.POST)	
    public String createInstance(@RequestBody Instance instance){	
        logger.info("Create Instance whose serviceId is {}", instance.getServiceId());	
        return "Instance whose serviceId is" + instance.getServiceId() + " is created";	
    }	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.PUT)	
    public String updateInstanceByServiceId(@RequestBody Instance instance, @PathVariable("serviceId") String serviceId){	
        logger.info("Update Instance whose serviceId is {}", serviceId);	
        return "Instance whose serviceId is " + serviceId + " is updated";	
    }	
}

上述代碼中通過 @RestController@RequestMapping聲明瞭四個網絡API接口,分別是對 Instance資源的增刪改查操作。

除了實現網絡API接口之外,還需要將該service註冊到 eureka上。如下列代碼所示,需要在 application.yml文件中設置服務註冊中心的相關信息和代表該應用的名稱。

eureka:	
  instance:	
    instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}	
  client:	
    service-url:	
      default-zone: http://localhost:8761/eureka/	
spring:	
  application:	
    name: feign-service	
server:	
  port: 0

服務消費者

Feign是聲明式RESTful客戶端,所以構建 Feign項目的關鍵在於構建服務消費者。通過下面六步可以創建一個 SpringCloudFeign的服務消費者。

首先創建一個普通的 SpringBoot工程,取名爲 chapter-feign-client。 然後在pom文件中添加 eurekafeign相關的依賴。其中 spring-cloud-starter-eurekaeureka的starter依賴包, spring-cloud-starter-feignfeign的starter依賴包。

<dependencies>	
    <dependency>	
        <groupId>org.springframework.cloud</groupId>	
        <artifactId>spring-cloud-starter-eureka</artifactId>	
    </dependency>	
    <dependency>	
        <groupId>org.springframework.cloud</groupId>	
        <artifactId>spring-cloud-starter-feign</artifactId>	
    </dependency>	
</dependencies>

接着在工程的入口類上添加 @EnableFeignClients註解表示開啓 SpringCloudFeign的支持功能,代碼如下所示。

@SpringBootApplication	
@EnableFeignClients()	
public class ChapterFeignClientApplication {	
    public static void main(String[] args) {	
        SpringApplication.run(ChapterFeignClientApplication.class, args);	
    }	
}

 @EnableFeignClients就像是一個開關,如果你使用了該註解,那麼 Feign相關的組件和處理機制纔會生效,否則不會生效。@EnableFeignClients還可以對 Feign相關組件進行自定義配置,它的方法和原理會在本章的源碼分析章節在做具體的講解。

接下來我們定義一個 FeignServiceClient接口,通過 @FeignClient註解來指定服務名進而綁定服務。這一類被 @FeignClient修飾的接口類一般被稱爲FeignClient。我們可以通過 @RequestMapping來修飾相應的方法來定義調用函數。

@FeignClient("feign-service")	
@RequestMapping("/feign-service")	
public interface FeignServiceClient {	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.GET)	
    public Instance getInstanceByServiceId(@PathVariable("serviceId") String serviceId);	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.DELETE)	
    public String deleteInstanceByServiceId(@PathVariable("serviceId") String serviceId);	
    @RequestMapping(value = "/instance", method = RequestMethod.POST)	
    public String createInstance(@RequestBody Instance instance);	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.PUT)	
    public String updateInstanceByServiceId(@RequestBody Instance instance, @PathVariable("serviceId") String serviceId);	
}

如上面代碼片段所顯示的,如果你調用 FeignServiceClient對象的 getInstanceByServiceId函數,那麼 Feign就會向 feign-service服務的 /feign-service/instance/{serviceId}接口發送網絡請求。

創建一個 Controller來調用上邊的服務,通過 @Autowired來自動裝載 FeignServiceClient示例。代碼如下:

@RestController	
@RequestMapping("/feign-client")	
public class FeignClientController {	
    @Autowired	
    FeignServiceClient feignServiceClient;	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.GET)	
    public Instance getInstanceByServiceId(@PathVariable("serviceId") String serviceId){	
        return feignServiceClient.getInstanceByServiceId(serviceId);	
    }	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.DELETE)	
    public String deleteInstanceByServiceId(@PathVariable("serviceId") String serviceId){	
        return feignServiceClient.deleteInstanceByServiceId(serviceId);	
    }	
    @RequestMapping(value = "/instance", method = RequestMethod.POST)	
    public String createInstance(@RequestBody Instance instance){	
        return feignServiceClient.createInstance(instance);	
    }	
    @RequestMapping(value = "/instance/{serviceId}", method = RequestMethod.PUT)	
    public String updateInstanceByServiceId(@RequestBody Instance instance, @PathVariable("serviceId") String serviceId){	
        return feignServiceClient.updateInstanceByServiceId(instance, serviceId);	
    }	
}

最後, application.yml中需要配置 eureka服務註冊中心的相關配置,具體配置如下所示:

eureka:	
  instance:	
    instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}	
  client:	
    service-url:	
      default-zone: http://localhost:8761/eureka/	
spring:	
  application:	
    name: feign-client	
server:	
  port: 8770

相信讀者通過搭建 Feign的項目,已經對 Feign的相關使用原理有了一定的瞭解,相信這個過程將對於理解 Feign相關的工作原理大有裨益。

-關注我

640?wx_fmt=png

推薦閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章