知識庫 / Web Services RSS 訂閱

REST vs. GraphQL vs. gRPC – 哪種 API 更適合你?

REST,Web Services
HongKong
8
03:37 AM · Dec 06 ,2025

1. 概述

REST 在設計 Web API 方面,多年來一直是行業事實標準的架構風格。然而,GraphQL 和 gRPC 近來出現,旨在解決 REST 的一些侷限性。每種這些 API 方案都具有顯著的優勢和一些權衡。

在本教程中,我們首先將研究每種 API 設計方法。然後,我們將使用 Spring Boot 構建一個簡單的服務,採用這三種不同的方法。接下來,我們將通過考察在決定採用哪一種方法之前應考慮的多個標準來比較它們。

最後,由於沒有一種萬能方法,我們將看到不同方法如何在不同的應用程序層之間混合使用。

2. REST

Representational State Transfer (REST) 是全球最常用的 API 架構風格。它由 Roy Fielding 於 2000 年定義。

2.1. 架構風格

REST 並非一個框架或庫,而是一種 基於 URL 結構和 HTTP 協議的架構風格,描述了一種接口。它描述了一種無狀態、可緩存、基於規範的客户端-服務器交互架構。它使用 URL 來定位資源,並使用 HTTP 方法來表達要執行的操作:

  • GET 用於獲取現有資源或多個資源
  • POST 用於創建新的資源
  • PUT 用於更新資源或如果不存在則創建它
  • DELETE 用於刪除資源
  • PATCH 用於部分更新現有資源

REST 可以用多種編程語言實現,並支持 JSON 和 XML 等多種數據格式。

2.2. 示例服務

我們可以通過定義使用 <em @RestController</em> 註解的控制器類來在 Spring 中構建 REST 服務。接下來,我們通過使用 <em @GetMapping</em> 註解定義與 HTTP 方法(例如 GET)對應的函數。最後,在註解參數中,我們提供一個方法應被觸發的資源路徑:

@GetMapping("/rest/books")
public List<Book> books() {
    return booksService.getBooks();
}

MockMvc 提供對 Spring 中 REST 服務集成測試的支持。它封裝了所有 Web 應用程序 Bean 並使它們可供測試使用:

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. 優缺點

REST的最大優勢在於它是技術世界中最成熟的API架構風格。由於其廣泛的普及,許多開發者已經熟悉REST,並且發現它易於使用。然而,由於其靈活性,REST可能會在不同開發者之間產生不同的解釋。

鑑於每個資源通常位於唯一的URL後面,因此很容易監控和限制API。REST還通過利用HTTP使緩存變得簡單。通過緩存HTTP響應,我們的客户端和服務器不需要不斷地相互交互。

REST容易出現下取數和上取數的問題。例如,為了獲取嵌套實體,我們可能需要進行多次請求。另一方面,在REST API中,通常不可能僅獲取特定實體數據的片段。客户端始終會接收請求端點配置返回的所有數據。

3. GraphQL

GraphQL 是一種由 Facebook 開發的開源 API 查詢語言。

3.1. 架構風格

GraphQL 提供了一種用於構建 API 的 查詢語言,並提供查詢執行框架。它不依賴 HTTP 方法來操作數據,主要使用 POST 方法。相比之下,GraphQL 使用查詢、修改和訂閲:

  • 查詢用於從服務器請求數據
  • 修改用於在服務器上修改數據
  • 訂閲用於在數據發生變化時獲取實時更新

GraphQL 是客户端驅動的,因為它允許客户端定義特定用例所需的精確數據。請求的數據將在一次往返中從服務器上檢索。

3.2. 示例服務

在 GraphQL 中,數據通過模式表示,模式定義了對象、它們的字段和類型。因此,我們將首先為我們的示例服務定義一個 GraphQL 模式:

type Author {
    firstName: String!
    lastName: String!
}

type Book {
    title: String!
    year: Int!
    author: Author!
}

type Query {
    books: [Book]
}

我們可以像構建 REST 服務一樣,通過使用 @RestController 類註解來構建 GraphQL 服務。接下來,我們使用 @QueryMapping 註解標記我們的函數,將其作為 GraphQL 數據獲取組件:

@QueryMapping
public List<Book> books() {
    return booksService.getBooks();
}

HttpGraphQlTester 提供對 Spring 中 GraphQL 服務集成測試的支持。它封裝了所有 Web 應用程序 Bean 並使它們可供測試使用:

this.graphQlTester.document(document)
  .execute()
  .path("books")
  .matchesJson(expectedJson);

GraphQL 服務可以使用 Postman 或 CURL 等工具進行測試。但是,它們需要將查詢指定在 POST 請求體中:

$ curl -X POST -H "Content-Type: application/json" -d "{\"query\":\"query{books{title}}\"}" http://localhost:8082/graphql

3.3. 優缺點

GraphQL 對客户端具有很高的靈活性,因為它 允許僅獲取和傳遞請求的數據。由於沒有發送過多的不必要數據到網絡上,因此 GraphQL 可以帶來更好的性能。

它使用比 REST 更嚴格的規範。此外,GraphQL 為調試目的提供詳細的錯誤描述,並自動生成 API 變更的文檔。

由於每個查詢可以不同,GraphQL 打破了中間代理緩存,使得緩存實現更加困難。 此外,由於一個 GraphQL 查詢可能執行一個大型且複雜的服務器端操作,因此查詢通常會受到複雜度的限制,以避免過載服務器。

4. gRPC

RPC 代表遠程過程調用,gRPC 是 Google 創建的高性能、開源 RPC 框架。

4.1. 架構風格

gRPC 框架基於遠程過程調用(RPC)的客户端-服務器模型。客户端應用程序可以直接調用服務器應用程序的方法,就像它是一個本地對象一樣。 這是一個基於合同的嚴格方法,客户端和服務器都需要訪問相同的模式定義。

在 gRPC 中,一種稱為協議緩衝區語言的 DSL 定義了請求和響應類型。 協議緩衝區編譯器然後生成服務器和客户端代碼工件。 我們可以使用生成的服務器代碼擴展自定義業務邏輯並提供響應數據。

該框架支持多種客户端-服務器交互類型:

  • 傳統的請求-響應交互
  • 服務器流式傳輸,其中一個客户端請求可能產生多個響應
  • 客户端流式傳輸,其中多個客户端請求導致單個響應

4.2. 示例服務

類似於 GraphQL,我們首先定義一個模式(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);
}

然後,我們需要將 protocol buffer 文件傳遞給 protocol buffer 編譯器以生成所需的代碼。我們可以手動執行此操作,使用預編譯的二進制文件之一,或者使用 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 類,並使用 @GrpcService 註解對其進行標註,然後覆蓋 books 方法:

@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();
}

gRPC 服務在 Spring 中的集成測試是可行的,但與 REST 和 GraphQL 相比,其成熟度仍有待提高。

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);

為了使此集成測試生效,我們需要對測試類進行以下註解:

  • @SpringBootTest,用於配置客户端連接到所謂的“內部” gRPC 測試服務器
  • @SpringJUnitConfig,用於準備和提供應用程序 Bean
  • @DirtiesContext,確保在每次測試後正確關閉服務器

Postman 最近增加了對 gRPC 服務的測試支持。 類似於 CURL,命令行工具 grpcurl 允許我們與 gRPC 服務器進行交互:

$ grpcurl --plaintext localhost:9090 com.baeldung.chooseapi.BooksService/books

該工具使用JSON編碼,使其protobuf編碼更易於人類理解,用於測試目的。

4.3. 優缺點

gRPC的最大優勢在於性能,這得益於其緊湊的數據格式、快速的消息編碼和解碼,以及HTTP/2的使用。 此外,其代碼生成功能支持多種編程語言,並能幫助我們節省編寫樣板代碼的時間。

通過要求使用HTTP 2和TLS/SSL,gRPC提供了更好的安全默認設置和內置的流式傳輸支持。 語言無關的接口合約定義使不同編程語言編寫的服務之間能夠進行通信。

然而,目前gRPC在開發者社區中的受歡迎程度遠不如REST。 它的數據格式對人類來説不可讀,因此需要額外的工具來分析報文負載和進行調試。 此外,HTTP/2僅在現代瀏覽器的最新版本中通過TLS支持。

5. 選擇合適的API

現在我們已經熟悉了這三種API的設計方法,接下來讓我們看看在決定使用哪一種時,我們應該考慮哪些標準。

5.1. 數據格式

REST 在請求和響應數據格式方面具有最大的靈活性。我們可以實現 REST 服務以支持一種或多種數據格式,例如 JSON 和 XML。

另一方面,GraphQL 定義了自己的查詢語言,用於請求數據。GraphQL 服務以 JSON 格式響應。雖然可以將響應轉換為其他格式,但這不太常見,並且可能會影響性能。

gRPC 框架使用協議緩衝區,一種自定義二進制格式。它對人類來説不可讀,但這也是 gRPC 性能高的一大原因。雖然它在多種編程語言中得到支持,但格式不能自定義。

5.2. 數據獲取

GraphQL 是從服務器獲取數據最有效率的 API 方法。 由於它允許客户端選擇要獲取的數據,因此通常不會在網絡上傳輸額外的冗餘數據。

REST 和 gRPC 不支持這種高級客户端查詢。 因此,除非在服務器端開發和部署新的端點或過濾器,否則可能會返回額外的冗餘數據。

5.3. 瀏覽器支持

REST 和 GraphQL API 在所有現代瀏覽器中得到支持。通常,JavaScript 客户端代碼用於從瀏覽器向服務器 API 發送 HTTP 請求。

對於 gRPC API,瀏覽器支持並非原生支持。但是,針對 Web 的 gRPC 擴展可用。它基於 HTTP 1.1,但並未提供所有 gRPC 功能。類似於 Java 客户端,gRPC for the web 需要瀏覽器客户端代碼生成一個 gRPC 客户端,該客户端從 protocol buffer 模式生成。

5.4. 代碼生成

GraphQL 需要添加額外的庫到核心框架中,例如 Spring。這些庫提供對 GraphQL 模式的處理、基於註解的編程以及 GraphQL 請求的處理。雖然從 GraphQL 模式生成代碼是可能的,但並非必需的。任何與 GraphQL 模式中定義的類型相匹配的自定義 POJO 都可以使用

gRPC 框架也需要添加額外的庫到核心框架中,以及強制的代碼生成步驟。協議緩衝區編譯器生成服務器和客户端樣板代碼,我們可以對其進行擴展。如果使用自定義 POJO,則需要將其映射到生成的協議緩衝區類型。

REST 是一種可以採用任何編程語言和各種 HTTP 庫實現的架構風格。它不使用任何預定義的模式,也不需要任何代碼生成。不過,使用 Swagger 或 OpenAPI 允許我們定義模式並生成代碼,如果需要的話。

5.5. 響應時間

由於其優化的二進制格式, gRPC 與 REST 和 GraphQL 相比,具有顯著更快的響應時間。 此外,在所有這三種方法中,都可以使用負載均衡將客户端請求平均分配到多個服務器上。

然而,此外,gRPC 默認使用 HTTP/2.0,這使得 gRPC 的延遲低於 REST 和 GraphQL API。 通過 HTTP/2.0,多個客户端可以同時發送多個請求,而無需建立新的 TCP 連接。 大多數性能測試報告稱,gRPC 比 REST 快大約 5 到甚至 10 倍。

5.6. 緩存

使用 REST 緩存請求和響應非常簡單成熟,因為它允許在 HTTP 級別緩存數據。每個 GET 請求都會暴露應用程序資源,這些資源可以被瀏覽器、代理服務器或 CDN 輕鬆緩存。

由於 GraphQL 默認使用 POST 方法,並且每個查詢可能不同,這使得緩存實現更加困難,尤其是在客户端和服務器地理位置相距較遠的情況下。一種可能的解決方案是使用 GET 方法發送查詢,並使用預計算並存儲在服務器上的持久化查詢。一些 GraphQL 中間件服務也提供緩存功能。

目前,gRPC 默認不支持緩存請求和響應。但是,可以實現自定義中間件層來緩存響應。

5.7. 預期用途

REST 是一種良好的選擇,適用於諸如域等領域,可以被描述為一組資源,而不是操作。 利用 HTTP 方法可以對這些資源執行標準 CRUD 操作。 憑藉 HTTP 語義,它對調用者來説直觀易懂,因此非常適合面向公共界面的接口。 REST 強大的緩存支持使其適用於具有穩定使用模式和分佈式用户的大型 API。

GraphQL 在公共 API 中是一個良好的選擇,尤其是在多個客户端需要不同的數據集時。 因此,GraphQL 客户端可以使用標準化的查詢語言來指定他們想要的數據。 它還適用於聚合來自多個來源的數據並將其提供給多個客户端的 API。

gRPC 框架在開發內部 API 時是一個良好的選擇,尤其是在微服務之間頻繁交互時。 它通常用於從低級代理(如不同物聯網設備)收集數據。 然而,由於其有限的瀏覽器支持,因此難以在面向客户的 Web 應用程序中使用。

6. 混合搭配 (Mix and Match)

每個API架構風格都有其優勢。然而,沒有一種方法適用於所有情況,我們選擇哪種方法將取決於我們的用例。

我們不必每次都做出單一選擇。我們還可以在解決方案架構中混合搭配不同的風格

如上所示例架構圖所示,不同的API風格可能在不同的應用層中得到應用。

7. 結論

在本文中,我們探討了用於設計 Web API 的三種流行架構風格:REST、GraphQL 和 gRPC。我們分析了每種風格的應用場景,並描述了它們的優勢和權衡。

我們通過在 Spring Boot 中構建一個簡單的服務,來探索了這三種方法的應用。此外,我們通過比較它們,並考慮了在選擇方法時應考慮的多個標準。最終,由於沒有一種方法適用於所有情況,我們還探討了如何在不同應用程序層混合使用不同的方法。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.