點擊上方"程序員歷小冰",選擇“置頂或者星標”
你的關注意義重大!
本文摘自筆者出版的書籍《Spring Cloud 微服務架構進階》
微服務是軟件系統架構上的一種設計風格,它倡導將一個原本獨立的服務系統分成多個小型服務,這些小型服務都在獨立的進程中運行,通過各個小型服務之間的協作來實現原本獨立系統的所有業務功能。小型服務基於多種跨進程的方式進行通信協作,而在 SpringCloud
架構中比較常見的跨進程的方式是RESTful HTTP請求和RPC調用。
RPC就是遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。比如說,計算機 A 上的進程調用另外一臺計算機 B 上的進程,其中 A 上的調用進程被掛起,而 B 上的被調用進程開始執行,當值返回給 A 時,A 進程繼續執行。調用方可以通過使用參數將信息傳送給被調用方,而後可以通過傳回的結果得到信息。而這一過程,對於開發人員來說是透明的。
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
只能支持基於文本的網絡請求,但是它可以簡化網絡請求的實現,方便編程人員快速構建自己的網絡請求架構。
如上圖所示,使用 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文件中添加 eureka
和 feign
相關的依賴。其中 spring-cloud-starter-eureka
是 eureka
的starter依賴包, spring-cloud-starter-feign
是 feign
的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
相關的工作原理大有裨益。
-關注我
推薦閱讀