1. 概述
在之前的云应用开发中,我们使用网关模式实现了两个核心功能:
✅ 服务隔离:将客户端与各个微服务解耦,避免跨域问题
✅ 服务发现:通过Eureka实现服务实例定位
本文将深入探讨如何利用网关模式通过单次请求聚合多个服务数据。为此,我们将在网关中集成Feign,简化服务间API调用。
关于OpenFeign的详细用法可参考这篇文章。Spring Cloud官方也提供了Spring Cloud Gateway项目实现该模式。
2. 环境搭建
打开网关服务的pom.xml
,添加OpenFeign依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
最新版本可在Maven Central获取
在GatewayApplication.java
中启用Feign客户端:
@EnableFeignClients
public class GatewayApplication { ... }
接下来为图书和评分服务创建Feign客户端。
3. OpenFeign客户端
3.1. 图书客户端
创建BooksClient.java
接口:
@FeignClient("book-service")
public interface BooksClient {
@RequestMapping(value = "/books/{bookId}", method = RequestMethod.GET)
Book getBookById(@PathVariable("bookId") Long bookId);
}
该接口指示Spring创建访问/books/{bookId}
接口的Feign客户端。调用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
}
3.2. 评分客户端
创建RatingsClient.java
接口:
@FeignClient("rating-service")
public interface RatingsClient {
@RequestMapping(value = "/ratings", method = RequestMethod.GET)
List<Rating> getRatingsByBookId(
@RequestParam("bookId") Long bookId,
@RequestHeader("Cookie") String session);
}
⚠️ 关键点:该接口受安全保护,必须传递用户会话信息。我们通过@RequestHeader
注解将session写入请求头(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 { ... }
注入Feign客户端:
private BooksClient booksClient;
private RatingsClient ratingsClient;
@Autowired
public CombinedController(
BooksClient booksClient,
RatingsClient ratingsClient) {
this.booksClient = booksClient;
this.ratingsClient = ratingsClient;
}
实现聚合接口:
@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
提取session并传递给评分服务,实现安全访问
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, gateway, book, rating),运行测试验证功能。
6. 总结
我们成功将OpenFeign集成到网关中,实现了定制化聚合接口。这种模式带来三大优势:
✅ 按需定制API:避免"一刀切"的REST设计
✅ 减少网络调用:客户端单次请求获取聚合数据
✅ 服务解耦:微服务可独立演进,保持专注
通过网关模式,我们能灵活适配不同客户端需求,同时保持服务架构的简洁性和可维护性。
完整代码示例请访问GitHub仓库