GraphQL 碰撞 Apache APISIX,提升 API 領域的安全與效能

Apache_APISIX發表於2022-03-08

本文介紹了 Apache APISIX 和 GraphQL 的特性,以及如何使用 API 閘道器 Apache APISIX 代理 GraphQL 請求,並提出解決實際場景痛點的方案。

背景資訊

GraphQL 是一個開源的、面向 API 而創造出來的資料查詢操作語言以及相應的執行環境。最初由Facebook 於 2012 年內部開發,2015 年公開發布。2018 年 11 月 7 日,Facebook 將 GraphQL 專案轉移到新成立的 GraphQL 基金會。

您可以把 GraphQL 類比為 SQL 查詢語句來理解,與 SQL 查詢語句相比,GraphQL 對 API 中的資料提供了一套易於理解的完整描述,讓客戶端能夠通過自定義的描述來準確獲得其所需要的資料。這也讓 API 能夠從容面對日益複雜的介面發展,並避免最終成為一個令人望而生畏的複雜介面。

Apache APISIX 作為雲原生閘道器,在設計之初就已經具備識別 GraphQL 語法的匹配能力。通過對請求中攜帶的 GraphQL 語句進行高效匹配,篩除異常流量,進一步保證安全性和提高系統效能。

場景解析

我們正處於大資料大流量的時代,Apache APISIX 和 GraphQL 可以通過結合的方式,形成共贏的局面。接下來舉一個場景具體說明。

本文將會討論微服務架構場景下的 Apache APISIX 和 GraphQL 的實際應用。

實際場景中遇到的問題

在專案進行到後期時,往往會出現業務複雜化、團隊人員流動性高等問題,而微服務架構已經成為解決這類問題的常見解決方案。在微服務架構中,GraphQL 暴露出的介面分為分散式和集中式兩種,然而只有集中式介面設計才能夠最大化體現 GraphQL 的優勢,但是在集中式介面設計中,所有的微服務對外暴露的是同一個介面,因此處理流量的路由就不能簡單地根據 URL 進行轉發,而是應該根據請求中包含的不同欄位進行轉發

由於 NGINX 處理請求時僅會處理 URL 以及一些引數,但是隻有解析請求引數中的查詢資訊才可以知道客戶端訪問的資源,從而進行路由轉發,因此這種路由轉發方式通過傳統的 NGINX 是無法完成的。在實際應用場景中,將 GraphQL 介面直接對外暴露非常危險,因此需要一個專業的高效能 API 閘道器保護 GraphQL 的介面。

解決方案

基於 Apache APISIX 安全、穩定、高效能的特性,增加 GraphQL 靈活的路由匹配規則是解決 GraphQL 集中式介面設計問題的最佳方案。

不支援在 Docs 外貼上 block

在此方案中,Apache APISIX 作為 API 閘道器部署在 GraphQL Server 之前,為整個後端系統提供了安全保障,並且 Apache APISIX 根據自身所具備的 GraphQL 匹配功能,篩選一部分請求之後再由 GraphQL Server 進行處理,使整個請求資源過程變得更高效。

得益於 Apache APISIX 的動態特性,您可以啟用限流限速、身份認證、可觀測性等外掛,無需重啟服務,進一步提高此方案的執行效率,方便運維工作。

此外,Apache APISIX 還可以針對不同的 graphql_operation 進行不同的許可權校驗、針對不同的 graphql_name 轉發到不同的 Upstream,具體細節將會在下文中進行描述。

綜上所述,Apache APISIX + GraphQL 的解決方案充分利用 GraphQL 搜尋優勢的同時也能擁有 Apache APISIX 作為 API 閘道器所具備的安全性和穩定性

GraphQL 在 Apache APISIX 中的應用

基本邏輯

不支援在 Docs 外貼上 block

目前 GraphQL 在 Apache APISIX 中的執行邏輯如下:

  1. Clients 向 Apache APISIX 發起帶有 GraphQL 語句的請求;
  2. Apache APISIX 匹配路由並提取預設的 GraphQL 資料;
  3. Apache APISIX 通過預設 GraphQL 資料對請求資料進行匹配;

    1. 匹配成功,Apache APISIX 將繼續轉發請求;
    2. 匹配失敗,Apache APISIX 將立刻終止請求。
  4. 是否存在外掛;

    1. 如果存在外掛,請求將繼續被外掛處理,處理完成後,繼續轉發到 GraphQL Server;
    2. 如果不存在外掛,請求將直接轉發到 GraphQL Server。

在 APISIX core 裡內部匹配中,Apache APISIX 通過 graphql-lua 庫實現對 GraphQL 的支援。Apache APISIX GraphQL 解析庫會先對攜帶 GraphQL 語法的請求進行解析,之後將解析後的請求與預設在 Apache APISIX 資料庫裡的配置資料進行匹配。如果匹配成功 Apache APISIX 會放行並轉發請求,反之則終止請求。

具體配置

Apache APISIX 目前支援通過 GraphQL 的一些屬性過濾路由:

  • graphql_operation
  • graphql_name
  • graphql_root_fields

GraphQL 的屬性與下面的所展示 GraphQL query 語句一一對應:

query getRepo {
    owner {
        name
    }
    repo {
        created
    }
}
  • graphql_operation 對應 query
  • graphql_name 對應 getRepo
  • graphql_root_fields 對應 ["owner", "repo"]

您可以通過如下示例為 Apache APISIX 設定一條路由來驗證對 GraphQL 的匹配能力:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
    "methods": ["POST"],
    "uri": "/_graphql",
    "vars": [
        ["graphql_operation", "==", "query"],
        ["graphql_name", "==", "getRepo"],
        ["graphql_root_fields", "has", "owner"]
    ],
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "192.168.1.200:4000": 1
        }
    }
}'

接下來使用帶有 GraphQL 語句的請求去訪問:

curl -H 'content-type: application/graphql' -X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
    owner {
        name
    }
    repo {
        created
    }
}'

如果匹配成功,則 Apache APISIX 繼續進行請求轉發。

HTTP/1.1 200 OK

反之,則終止請求。

HTTP/1.1 404 Not Found

進階操作

Apache APISIX 可以根據不同的 graphql_name 轉發到不同的 Upstream,以及根據不同 graphql_operation 進行不同的許可權校驗。下文將為您展示此特性的程式碼配置。

使用 graphql_name 匹配 Upstream

  1. 建立第一個上游服務:
curl http://192.168.1.200:9080/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "type": "chash",
    "key": "remote_addr",
    "nodes": {
        "192.168.1.200:1980": 1
    }
}'   
  1. 建立與第一個上游服務繫結的 GraphQL 路由,graphql_name 設定為 getRepo111
curl http://192.168.1.200:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
    "methods": ["POST"],
    "uri": "/graphql",
    "vars": [
        ["graphql_operation", "==", "query"],
        ["graphql_name", "==", "getRepo111"],
        ["graphql_root_fields", "has", "owner"]
    ],    
    "upstream_id": "1"
}'   
  1. 建立第二個上游服務:
curl http://192.168.1.200:9080/apisix/admin/upstreams/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "type": "chash",
    "key": "remote_addr",
    "nodes": {
        "192.168.1.200:1981": 1
    }
}'
  1. 建立與第二個上游服務繫結的 GraphQL 路由,graphql_name 設定為 getRepo222
curl http://192.168.1.200:9080/apisix/admin/routes/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
    "methods": ["POST"],
    "uri": "/graphql",
    "vars": [
        ["graphql_operation", "==", "query"],
        ["graphql_name", "==", "getRepo222"],
        ["graphql_root_fields", "has", "owner"]
    ],
    "upstream_id": 2
}'
  1. 測試示例

使用之前所建立的兩個 graphql_name 服務進行測試,您可以發現 Apache APISIX 可以根據請求中不同的 graphql_name 自動選擇轉發的 Upstream。

  1. 如果請求是此示例:
curl -i -H 'content-type: application/graphql' -X POST http://192.168.1.200:9080/graphql -d '
query getRepo111 {
    owner {
        name
    }
    repo {
        created
    }
}'

則會返回來自上游 192.168.1.200:1980 的響應:

HTTP/1.1 200 OK
---URI
/graphql
---Service Node
Centos-port: 1980
  1. 如果請求是這個示例:
curl -i -H 'content-type: application/graphql' -X POST http://192.168.1.200:9080/graphql -d '
query getRepo222 {
    owner {
        name
    }
    repo {
        created
    }
}'

則會返回來自上游 192.168.1.200:1981 的響應:

HTTP/1.1 200 OK
---URI
/graphql
---Service Node
Centos-port: 1981

使用 graphql_operation 進行不同的許可權校驗

上述示例提供了 graphql_operationquery 的匹配規則,現在使用 mutation 形式的 GraphQL 請求。

  1. 配置 Apache APISIX:
curl http://192.168.1.200:9080/apisix/admin/routes/11 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "methods": ["POST"],
    "uri": "/hello",
    "vars": [
        ["graphql_operation", "==", "mutation"],
        ["graphql_name", "==", "repo"]
    ],
    "upstream": {
        "nodes": {
            "192.168.1.200:1982": 1
        },
        "type": "roundrobin"
    }
}'
  1. 發起 mutation 請求用來驗證 Apache APISIX 的配置:
curl -i -H 'content-type: application/graphql' -X POST http://192.168.1.200:9080/hello -d '
mutation repo($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}'

返回結果如下所示:

HTTP/1.1 200 OK
---URI
/hello
---Service Node
Centos-port: 1982

搭配外掛

Apache APISIX 擁有豐富的外掛生態以應用不同的使用場景,如果在使用 Apache APISIX + GraphQL 時新增適合的外掛,就可以使方案應用的場景變得更多。

本文僅選取了以下兩種型別的外掛進行舉例。

limit-count 速度外掛

搭配使用 limit-count 外掛,流量經過 GraphQL 匹配規則得到轉發之後進一步被限制。得益於 Apache APISIX 的特性,可以達到動態、精細化、分散式的限流限速。詳情請參考 Apache APISIX 官方文件

可觀測性外掛

Apache APISIX 提供了包括但不僅限於 prometheusskywalking 等可觀測性外掛,能夠為系統提供更多監控指標資料,方便系統後續的運營維護等實現。

總結

本文為大家初步介紹了 GraphQL 在 Apache APISIX 中的應用,並且使用實際程式碼,為您展示了Apache APISIX 與 GraphQL 兩項技術的結合,使用者可以根據自身的業務需求和實際場景在 Apache APISIX 中使用 GraphQL。

關於 GraphQL 的更多說明和完整配置資訊,可參考官方文件

Apache APISIX 專案目前正在開發其他外掛以支援整合更多服務,如果您對此有興趣,您可以通過 GitHub Discussions 發起討論,或通過郵件列表進行交流。

相關文章