1. 概述
多年来,REST 一直是 Web API 设计的事实标准。但 GraphQL 和 gRPC 的出现解决了 REST 的一些局限性。每种 API 方案都有显著优势,也存在相应权衡。
本文将先分析三种 API 设计方案,然后用 Spring Boot 分别实现一个简单服务。接着通过多个关键维度对比它们,最后说明如何在不同应用层混合使用这些方案。
2. REST
REST(表述性状态转移)是全球最常用的 API 架构风格,由 Roy Fielding 在 2000 年提出。
2.1. 架构风格
REST 不是框架或库,而是一种基于 URL 结构和 HTTP 协议的接口设计风格。它描述了客户端-服务器交互的无状态、可缓存、基于约定的架构。通过 URL 定位资源,HTTP 方法表达操作:
- GET:获取单个或多个资源
- POST:创建新资源
- PUT:更新资源(不存在则创建)
- DELETE:删除资源
- PATCH:部分更新资源
REST 支持多种编程语言和数据格式(如 JSON/XML)。
2.2. 示例服务
在 Spring 中构建 REST 服务,使用 @RestController
注解定义控制器类。通过 @GetMapping
等注解绑定 HTTP 方法和资源路径:
@GetMapping("/rest/books")
public List<Book> books() {
return booksService.getBooks();
}
使用 MockMvc
进行集成测试,它封装了所有 Web 应用组件:
this.mockMvc.perform(get("/rest/books"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json(expectedJson));
基于 HTTP 的 REST 服务可通过浏览器或工具(Postman/CURL)测试:
$ curl http://localhost:8082/rest/books
2.3. 优缺点
✅ 最大优势:技术圈最成熟的 API 架构风格,开发者熟悉度高
❌ 灵活性陷阱:不同开发者对 REST 的理解可能存在差异
✅ 监控友好:每个资源有独立 URL,便于限流和监控
✅ 缓存简单:直接利用 HTTP 缓存机制,减少客户端-服务器交互
❌ 数据获取问题:
- 欠获取(Under-fetching):获取嵌套实体需多次请求
- 过获取(Over-fetching):无法按需获取字段,总是返回完整数据
3. GraphQL
GraphQL 是 Facebook 开发的开源 API 查询语言。
3.1. 架构风格
GraphQL 提供查询语言和执行框架。不依赖 HTTP 方法操作数据(主要用 POST),而是通过:
- 查询(Query):请求数据
- 变更(Mutation):修改数据
- 订阅(Subscription):实时数据更新
客户端驱动:客户端可精确声明所需数据,单次请求获取所有信息。
3.2. 示例服务
GraphQL 中数据通过 Schema 定义(对象、字段、类型):
type Author {
firstName: String!
lastName: String!
}
type Book {
title: String!
year: Int!
author: Author!
}
type Query {
books: [Book]
}
Spring 实现类似 REST,用 @RestController
和 @QueryMapping
注解:
@QueryMapping
public List<Book> books() {
return booksService.getBooks();
}
使用 HttpGraphQlTester
进行集成测试:
this.graphQlTester.document(document)
.execute()
.path("books")
.matchesJson(expectedJson);
测试需在 POST 请求体中指定查询:
$ curl -X POST -H "Content-Type: application/json" -d "{\"query\":\"query{books{title}}\"}" http://localhost:8082/graphql
3.3. 优缺点
✅ 按需获取:仅返回客户端请求的数据,减少网络传输
✅ 规范严格:相比 REST 的模糊性,提供更明确的规范
✅ 调试友好:详细错误描述 + 自动生成变更文档
❌ 缓存困难:查询多样性导致中间代理缓存失效
⚠️ 性能风险:复杂查询可能压垮服务器,需限制查询复杂度
4. gRPC
gRPC 是 Google 开发的高性能开源 RPC 框架。
4.1. 架构风格
基于客户端-服务器模型的远程过程调用(RPC)。客户端像调用本地方法一样直接调用服务器方法。强契约模式:客户端和服务器需共享相同 Schema 定义。
使用 Protocol Buffers(protobuf)定义请求/响应类型,编译器生成服务器和客户端代码。支持多种交互模式:
- 传统请求-响应
- 服务端流(单请求多响应)
- 客户端流(多请求单响应)
通过 HTTP/2 传输紧凑的二进制格式,编解码效率极高。
4.2. 示例服务
首先定义 Schema(服务、请求/响应结构):
message BooksRequest {}
message AuthorProto {
string firstName = 1;
string lastName = 2;
}
message BookProto {
string title = 1;
AuthorProto author = 2;
int32 year = 3;
}
message BooksResponse {
repeated BookProto book = 1;
}
service BooksService {
rpc books(BooksRequest) returns (BooksResponse);
}
通过 protobuf-maven-plugin
生成代码:
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
扩展生成的 BooksServiceImplBase
类:
@Override
public void books(BooksRequest request, StreamObserver<BooksResponse> responseObserver) {
List<Book> books = booksService.getBooks();
BooksResponse.Builder responseBuilder = BooksResponse.newBuilder();
books.forEach(book -> responseBuilder.addBook(GrpcBooksMapper.mapBookToProto(book)));
responseObserver.onNext(responseBuilder.build());
responseObserver.onCompleted();
}
集成测试需特殊注解(@SpringBootTest
+ @SpringJUnitConfig
+ @DirtiesContext
):
BooksRequest request = BooksRequest.newBuilder().build();
BooksResponse response = booksServiceGrpc.books(request);
List<Book> books = response.getBookList().stream()
.map(GrpcBooksMapper::mapProtoToBook)
.collect(Collectors.toList());
JSONAssert.assertEquals(objectMapper.writeValueAsString(books), expectedJson, true);
使用 grpcurl
工具测试:
$ grpcurl --plaintext localhost:9090 com.baeldung.chooseapi.BooksService/books
4.3. 优缺点
✅ 性能卓越:二进制格式 + 高效编解码 + HTTP/2
✅ 代码生成:支持多语言,减少样板代码
✅ 安全默认:强制 HTTP/2 + TLS/SSL
✅ 跨语言支持:语言无关的契约定义
❌ 生态局限:开发者社区接受度低于 REST
❌ 调试困难:二进制格式不可读,需专用工具
⚠️ 浏览器限制:现代浏览器仅通过 TLS 支持 HTTP/2
5. 如何选择API方案
5.1. 数据格式
- REST:最灵活,支持 JSON/XML 等多种格式
- GraphQL:使用专用查询语言,响应为 JSON(转换其他格式影响性能)
- gRPC:强制使用 Protocol Buffers(二进制格式,不可定制)
5.2. 数据获取
- GraphQL:最高效,客户端精确控制返回字段
- REST/gRPC:需开发新接口或过滤器才能避免过获取/欠获取
5.3. 浏览器支持
- REST/GraphQL:所有现代浏览器原生支持
- gRPC:需 gRPC-Web 扩展(基于 HTTP 1.1,功能受限)
5.4. 代码生成
- GraphQL:需额外库,代码生成可选(可用任意匹配 Schema 的 POJO)
- gRPC:强制代码生成步骤,需映射自定义 POJO 到生成类型
- REST:无强制要求,可通过 OpenAPI 生成代码
5.5. 响应时间
- gRPC:显著更快(二进制格式 + HTTP/2),性能测试显示比 REST 快 5-10 倍
- REST/GraphQL:基于 HTTP 1.1,延迟较高
5.6. 缓存
- REST:简单成熟,直接利用 HTTP 缓存(浏览器/代理/CDN)
- GraphQL:默认困难(POST + 动态查询),需持久化查询等变通方案
- gRPC:无原生支持,需自定义中间件
5.7. 适用场景
- REST:适合资源导向的领域(如 CRUD 操作),公共接口首选,缓存友好
- GraphQL:适合多客户端需不同数据集的公共 API,或聚合多数据源的场景
- gRPC:适合微服务间高频通信,或 IoT 设备等低层数据采集,不推荐面向客户的 Web 应用
6. 混合使用方案
没有万能方案,可在架构中混合使用不同风格:
上图展示了不同应用层使用不同 API 风格的典型架构。
7. 总结
本文深入分析了三种主流 Web API 架构风格:REST、GraphQL 和 gRPC。我们通过 Spring Boot 实现了相同服务的三种版本,从数据格式、性能、缓存等维度对比了它们的优劣。
关键结论:
- REST 仍是公共 API 的安全选择
- GraphQL 解决了数据获取痛点,适合复杂前端场景
- gRPC 在微服务通信中性能无敌
最终选择应基于具体需求:没有银弹,只有最适合当前场景的方案。必要时可混合使用,发挥各自优势。
完整源码见 GitHub 仓库