Spring Cloud系列–網關模式

1.概述

到目前為止,在我們的雲應用程序中,我們已經使用了網關模式來支持兩個主要功能。

首先,我們將客戶與每項服務隔離開,從而消除了跨域支持的需求。接下來,我們使用Eureka實現了服務定位實例。

在本文中,我們將研究如何使用網關模式通過單個請求從多個服務中檢索數據。為此,我們將Feign引入網關,以幫助將API調用寫入我們的服務。

要閱讀有關如何使用Feign客戶端的信息,請查看本文。

Spring Cloud現在還提供了實現此模式的Spring Cloud Gateway項目。

2.設定

讓我們打開gateway服務器的pom.xml並添加Feign的依賴項:

<dependency>

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

 <artifactId>spring-cloud-starter-feign</artifactId>

 </dependency>

供參考–我們可以在Maven Centralspring-cloud-starter-feign )上找到最新版本。

現在我們已經支持構建Feign客戶端,讓我們在GatewayApplication.java啟用它:

@EnableFeignClients

 public class GatewayApplication { ... }

現在,讓我們為圖書和評級服務設置Feign客戶。

3.假客戶

3.1。圖書客戶

讓我們創建一個名為BooksClient.java的新接口:

@FeignClient("book-service")

 public interface BooksClient {



 @RequestMapping(value = "/books/{bookId}", method = RequestMethod.GET)

 Book getBookById(@PathVariable("bookId") Long bookId);

 }

通過這個接口,我們指示Spring創建一個Feign客戶端,該客戶端將訪問“ /books/{bookId }”端點。調用時, getBookById方法將對端點進行HTTP調用,並使用bookId參數。

為了完成這項工作,我們需要添加Book.java DTO:

@JsonIgnoreProperties(ignoreUnknown = true)

 public class Book {



 private Long id;

 private String author;

 private String title;

 private List<Rating> ratings;



 // getters and setters

 }

讓我們繼續到RatingsClient

3.2。評級客戶

讓我們創建一個稱為RatingsClient的接口:

@FeignClient("rating-service")

 public interface RatingsClient {



 @RequestMapping(value = "/ratings", method = RequestMethod.GET)

 List<Rating> getRatingsByBookId(

 @RequestParam("bookId") Long bookId,

 @RequestHeader("Cookie") String session);



 }

BookClient一樣,此處公開的方法將對我們的評級服務進行重新調用,並返回一本書的評級列表。

但是,此端點是安全的。為了能夠正確訪問此端點,我們需要將用戶的會話傳遞給請求。

我們使用@RequestHeader批註進行此操作。這將指示Feign將變量的值寫入請求的標頭。在本例中,我們正在寫Cookie頭,因為Spring Session將在cookie中尋找我們的會話。

在本例中,我們正在寫Cookie頭,因為Spring Session將在cookie中尋找我們的會話。

最後,讓我們添加一個Rating.java DTO:

@JsonIgnoreProperties(ignoreUnknown = true)

 public class Rating {

 private Long id;

 private Long bookId;

 private int stars;

 }

現在,兩個客戶端均已完成。讓我們使用它們!

4.合併請求

網關模式的一種常見用例是使端點封裝通常稱為服務的端點。通過減少客戶端請求的數量,可以提高性能。

為此,我們創建一個控制器並將其命名為CombinedController.java

@RestController

 @RequestMapping("/combined")

 public class CombinedController { ... }

接下來,讓我們連接新創建的偽裝客戶:

private BooksClient booksClient;

 private RatingsClient ratingsClient;



 @Autowired

 public CombinedController(

 BooksClient booksClient,

 RatingsClient ratingsClient) {



 this.booksClient = booksClient;

 this.ratingsClient = ratingsClient;

 }

最後,讓我們創建一個GET請求,該請求將這兩個端點結合起來,並返回加載了其評分的一本書:

@GetMapping

 public Book getCombinedResponse(

 @RequestParam Long bookId,

 @CookieValue("SESSION") String session) {



 Book book = booksClient.getBookById(bookId);

 List<Rating> ratings = ratingsClient.getRatingsByBookId(bookId, "SESSION="+session);

 book.setRatings(ratings);

 return book;

 }

注意,我們正在使用@CookieValue註釋設置會話值,該註釋將從請求中提取出來。

在那裡!我們在網關中有一個組合的端點,可以減少客戶端與系統之間的網絡調用!

5.測試

確保我們的新端點正常運行。

導航到LiveTest.java ,讓我們為組合的端點添加一個測試:

@Test

 public void accessCombinedEndpoint() {

 Response response = RestAssured.given()

 .auth()

 .form("user", "password", formConfig)

 .get(ROOT_URI + "/combined?bookId=1");



 assertEquals(HttpStatus.OK.value(), response.getStatusCode());

 assertNotNull(response.getBody());



 Book result = response.as(Book.class);



 assertEquals(new Long(1), result.getId());

 assertNotNull(result.getRatings());

 assertTrue(result.getRatings().size() > 0);

 }

啟動Redis,然後在我們的應用程序中運行每個服務: config, discovery, zipkin, gatewaybookrating服務。

一切就緒後,請運行新測試以確認其正常運行。

六,結論

我們已經看到瞭如何將Feign集成到我們的網關中以構建專用端點。我們可以利用這些信息來構建我們需要支持的任何API。最重要的是,我們看到我們並沒有被只暴露單個資源的“一刀切”的API所困。

使用網關模式,我們可以根據每個客戶的需求來設置我們的網關服務。這樣就產生了去耦,使我們的服務可以自由地根據需要進行發展,保持精簡併專注於應用程序的一個領域。

與往常一樣,可以在GitHub上找到代碼段