1. 程式人生 > >【SFA官方翻譯】Spring WebFlux和Spring Cloud進行響應式微服務開發

【SFA官方翻譯】Spring WebFlux和Spring Cloud進行響應式微服務開發

啟用 測試數據 技術 logger 轉發 bic snap uri led

原創 SpringForAll社區 2018-05-18

作者 Spring4all 社區

摘要:

如果你想用Spring的最新和最好的工具開始使用響應式微服務,那麽這篇文章就是為你準備的! 我已經在一年前的 Reactive microservices with Spring 5 這篇文章中描述了Spring對反應式的支持。當時,Spring WebFlux項目一直處於積極的發展階段,現在,在Spring 5正式發布之後,它在這個版本是非常值得關註的。 此外,我們將嘗試把反應式微服務放入Spring Cloud組件中,其中包含諸如Eureka服務發現,使用Spring Cloud Commons

@LoadBalanced進行負載均衡以及使用Spring Cloud Gateway(也基於WebFluxNetty)的API網關等元素,。 我們還將以Spring Data Reactive Mongo項目為例,介紹Spring對NoSQL數據庫的反應性支持。 我們的體系結構示例圖如下,它包含兩個微服務,一個服務發現,一個網關和MongoDB數據庫。 源代碼sample-spring-cloud-webflux通常在GitHub上的一樣可用。 技術分享圖片

接下來,我們進一步來描述創建上述系統的步驟。

第1步:使用Spring WebFlux構建反應式應用程序

為了在項目中使用庫Spring WebFlux,我們應該將 spring-boot-starter-webflux

添加到依賴關系中。 它包括一些依賴庫,如Reactor或Netty服務器。

  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-webflux</artifactId>

  4. </dependency>

REST控制器看起來與同步Web服務定義的控制器非常相似。 唯一的區別在於返回對象的類型。 如果我們返回單個對象,將使用Mono類的實例,如果我們返回的是多個對象,比如是一個列表,將返回一個Flux類的實例。 由於Spring Data Reactive Mongo

,我們不需要做任何事情,只需調用相應接口上的所需方法即可。 實例代碼如下:

  1. @RestController

  2. public class AccountController {

  3. private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);

  4. @Autowired

  5. private AccountRepository repository;

  6. @GetMapping("/customer/{customer}")

  7. public Flux findByCustomer(@PathVariable("customer") String customerId) {

  8. LOGGER.info("findByCustomer: customerId={}", customerId);

  9. return repository.findByCustomerId(customerId);

  10. }

  11. @GetMapping

  12. public Flux findAll() {

  13. LOGGER.info("findAll");

  14. return repository.findAll();

  15. }

  16. @GetMapping("/{id}")

  17. public Mono findById(@PathVariable("id") String id) {

  18. LOGGER.info("findById: id={}", id);

  19. return repository.findById(id);

  20. }

  21. @PostMapping

  22. public Mono create(@RequestBody Account account) {

  23. LOGGER.info("create: {}", account);

  24. return repository.save(account);

  25. }

  26. }

第2步:使用Spring Data Reactive Mongo將應用程序與數據庫集成

應用程序和數據庫之間的集成實現也非常簡單。 首先,我們需要在項目依賴項中添加相關數據庫依賴 spring-boot-starter-data-mongodb-reactive

  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>

  4. </dependency>

添加依賴過後,Mongo將自動支持響應式應用。 下一步是使用ORM映射聲明一個實體。 以下類也作為AccountController的響應返回。

  1. @Document

  2. public class Account {

  3. @Id

  4. private String id;

  5. private String number;

  6. private String customerId;

  7. private int amount;

  8. ...

  9. }

最後,我們可以創建一個擴展ReactiveCrudRepository的接口。 它遵循Spring Data JPA實現的模式,並提供了一些CRUD操作的基本方法。 它還允許我們自定義方法,這些名稱會自動映射到查詢。 與標準Spring Data JPA庫相比唯一的區別在於方法簽名。 這些對象將由Mono和Flux進行包裝。

  1. public interface AccountRepository extends ReactiveCrudRepository {

  2. Flux findByCustomerId(String customerId);

  3. }

在這個例子中,我使用了Docker容器在本地運行MongoDB。 因為我使用Docker Toolkit在Windows上運行Docker,所以Docker機器的默認地址是192.168.99.100。 這是application.yml文件中數據源的配置。

  1. spring:

  2. data:

  3. mongodb:

  4. uri: mongodb://192.168.99.100/test

步驟3:使用Eureka啟用服務發現

SpringCloudEureka的集成非常類似於傳統的REST微服務。 要啟用發現客戶端功能,我們應該首先將啟動器 spring-cloud-starter-netflix-eureka-client添加到項目依賴項中。

  1. <dependency>

  2. <groupId>org.springframework.cloud</groupId>

  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

  4. </dependency>

然後我們必須使用 @EnableDiscoveryClient這個註解來啟用它的功能。

  1. @SpringBootApplication

  2. @EnableDiscoveryClient

  3. public class AccountApplication {

  4. public static void main(String[] args) {

  5. SpringApplication.run(AccountApplication.class, args);

  6. }

  7. }

微服務將自動在Eureka註冊中心進行註冊。 當然,我們可能會運行每個服務的多個實例。 以下是運行 account-service實例和 customer-service服務實例後的Eureka Dashboard 儀表板界面(http:// localhost:8761)。 這裏將不詳細講解使用嵌入式Eureka服務器運行應用程序的細節。 有關詳細信息,請參閱我之前的文章,Spring Boot 2.0的微服務快速指南,Eureka和Spring Cloud。 Eureka服務器可作為 adiscovery-service模塊使用。 技術分享圖片

第4步:使用WebClient進行反應性微服務之間的服務間通信

Spring WebFlux項目中的WebClient實現了一個服務間通信。 與RestTemplate相同,您應該使用Spring Cloud Commons @LoadBalanced對其進行註解。 它支持使用Netflix OSS Ribbon客戶端與服務發現和負載均衡進行集成。 所以,第一步是使用 @LoadBalanced註解聲明一個客戶端構建器。

  1. @Bean

  2. @LoadBalanced

  3. public WebClient.Builder loadBalancedWebClientBuilder() {

  4. return WebClient.builder();

  5. }

然後我們可以將WebClientBuilder註入到REST控制器中。 通過GET / {id} / with-accounts實現與 account-service通信,首先我們使用一個基於響應式的Spring Data repository來搜索客戶實體。 它返回Mono對象,而WebClient返回Flux。 現在,我們的主要目的是將這些內容合並到訂閱者,並從Flux中返回一個包含帳戶列表的Mono對象。 下面的代碼片段說明了我如何使用WebClient與另一個微服務進行通信,然後將響應和結果合並到單個Mono對象。 這種合並可以用更“優雅”的方式完成,所以你可以隨意創建一個推送請求。

  1. @Autowired

  2. private WebClient.Builder webClientBuilder;

  3. @GetMapping("/{id}/with-accounts")

  4. public Mono findByIdWithAccounts(@PathVariable("id") String id) {

  5. LOGGER.info("findByIdWithAccounts: id={}", id);

  6. Flux accounts = webClientBuilder.build().get().uri("http://account-service/customer/{customer}", id).retrieve().bodyToFlux(Account.class);

  7. return accounts

  8. .collectList()

  9. .map(a -> new Customer(a))

  10. .mergeWith(repository.findById(id))

  11. .collectList()

  12. .map(CustomerMapper::map);

  13. }

第5步:使用Spring Cloud Gateway構建API網關

Spring Cloud Gateway是最新的Spring Cloud項目之一。 它建立在Spring WebFlux的基礎之上,並且由於這一點,我們可以將它用作基於反應式微服務的入口。 與Spring WebFlux應用程序類似,它在嵌入式Netty服務器上運行。 要使用Spring Boot應用程序啟用它,只需在您的項目中包含以下依賴項。

  1. <dependency>

  2. <groupId>org.springframework.cloud</groupId>

  3. <artifactId>spring-cloud-starter-gateway</artifactId>

  4. </dependency>

我們還應該啟用發現客戶端,以便網關能夠獲取已註冊的微服務列表。 但是,不需要在Eureka中註冊網關的應用程序。 要禁用註冊,可以在application.yml文件中將屬性 eureka.client.registerWithEureka設置為 false

  1. @SpringBootApplication

  2. @EnableDiscoveryClient

  3. public class GatewayApplication {

  4. public static void main(String[] args) {

  5. SpringApplication.run(GatewayApplication.class, args);

  6. }

  7. }

默認情況下,Spring Cloud Gateway不支持與服務發現的集成。要啟用它,我們應該將屬性 spring.cloud.gateway.discovery.locator.enabled設置為true。現在,應該完成的最後一件事情就是路由器的配置。 Spring Cloud Gateway提供了兩種可以在路由中配置的組件:filters(過濾器)和predicates(謂詞)。 Predicates用於將HTTP請求與路由進行匹配,而過濾器可用於在發送請求之前或之後修改請求和響應。這是網關的完整配置。它啟用服務發現位置,並根據服務註冊表中的條目定義兩種路由。我們使用Path Route Predicate工廠來匹配傳入的請求,並使用RewritePath GatewayFilter工廠來修改請求的路徑,以使其匹配相應的服務格式(端點顯示在路徑/下,而網關將它們暴露在路徑下/account 和/customer下)。

  1. spring:

  2. cloud:

  3. gateway:

  4. discovery:

  5. locator:

  6. enabled: true

  7. routes:

  8. - id: account-service

  9. uri: lb://account-service

  10. predicates:

  11. - Path=/account/**

  12. filters:

  13. - RewritePath=/account/(?.*), /$\{path}

  14. - id: customer-service

  15. uri: lb://customer-service

  16. predicates:

  17. - Path=/customer/**

  18. filters:

  19. - RewritePath=/customer/(?.*), /$\{path}

第6步:測試樣本系統

在做一些測試之前,讓我們回顧一下我們的示例系統。 我們有兩個微服務 - account-service, customer-service - 使用MongoDB作為數據庫。 微服務customer-service調用account-service暴露的端點GET / customer / {customer}。 account-service的URL來自Eureka。 整個系統隱藏在網關後面,該網關位於localhost:8090的地址下。 現在,第一步是在Docker容器上運行MongoDB。 執行以下命令後,Mongo在地址192.168.99.100:27017下可用。

  1. $ docker run -d --name mongo -p 27017:27017 mongo

然後我們可以繼續運行 discovery-service。 Eureka在其默認地址localhost:8761下可用。 您可以使用IDE運行它,或者執行命令 java-jar target/discovery-service-1.0-SNAPHOT.jar。 同樣的適用於我們的示例微服務。 但是,account-service需要在兩個實例中進行通信,所以當使用-Dserver.port VM參數運行第二個實例時,您需要覆蓋默認的HTTP端口,例如 java-jar-Dserver.port=2223target/account-service-1.0-SNAPSHOT.jar。 最後,在運行網關服務之後,我們可以添加一些測試數據。

  1. $ curl --header "Content-Type: application/json" --request POST --data ‘{"firstName": "John","lastName": "Scott","age": 30}‘ http://localhost:8090/customer

  2. {"id": "5aec1debfa656c0b38b952b4","firstName": "John","lastName": "Scott","age": 30,"accounts": null}

  3. $ curl --header "Content-Type: application/json" --request POST --data ‘{"number": "1234567890","amount": 5000,"customerId": "5aec1debfa656c0b38b952b4"}‘ http://localhost:8090/account

  4. {"id": "5aec1e86fa656c11d4c655fb","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 5000}

  5. $ curl --header "Content-Type: application/json" --request POST --data ‘{"number": "1234567891","amount": 12000,"customerId": "5aec1debfa656c0b38b952b4"}‘ http://localhost:8090/account

  6. {"id": "5aec1e91fa656c11d4c655fc","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 12000}

  7. $ curl --header "Content-Type: application/json" --request POST --data ‘{"number": "1234567892","amount": 2000,"customerId": "5aec1debfa656c0b38b952b4"}‘ http://localhost:8090/account

  8. {"id": "5aec1e99fa656c11d4c655fd","number": "1234567892","customerId": "5aec1debfa656c0b38b952b4","amount": 2000}

要測試服務間通信,只需在網關服務上調用端點 GET/customer/{id}/with-accounts。 它將請求轉發給customer-service,然後customer-service使用響應式WebClient調用由account-service 暴露的端點。 結果如下所示。 技術分享圖片

結論

Spring 5和Spring Boot 2.0的出現,帶來了許多可用的方法來構建基於微服務的體系結構。我們可以使用與Spring Cloud Netflix項目的一對一通信,基於消息代理的消息傳遞微服務,以及與Spring Cloud Stream的發布/訂閱通信模型構建標準同步系統,最後使用Spring實現異步,反應式微服務WebFlux。本文的主要目標是向您展示如何將Spring WebFlux與Spring Cloud項目一起使用,以便為構建在Spring Boot之上的響應式微服務提供服務發現,負載均衡或API網關等機制。在Spring 5之前,缺乏對響應式微服務的支持是Spring框架的缺點之一,但現在,Spring WebFlux已不再是這種情況。不僅如此,我們還可以利用Spring對最受歡迎的NoSQL數據庫(如MongoDB或Cassandra)的反應式支持,並輕松地將我們的反應式微服務與同步REST微服務一起放入一個系統中。

【SFA官方翻譯】Spring WebFlux和Spring Cloud進行響應式微服務開發