通过 API 网关公开 Appsync 订阅时出现 AmplifyJS 错误

AmplifyJS error when Appsync subscribe is exposed via API Gateway

Appsync 已经在某种程度上取代了 API 网关,那么您为什么需要通过 API 网关公开它。我知道大多数人都会问这个问题。这是为什么?

  1. 支持使用计划
  2. 货币化的可能性。

据我了解,Appsync 是 GrapQL + Apollo 服务器实现。 API 公开支持 POST 请求。甚至订阅请求也是一个 post 请求,以 Websocket MQTT (AWS IoT) URL 作为响应。 (例如下面提供的)

{
  "extensions": {
    "subscription": {
      "mqttConnections": [
        {
          "url": "wss://something-ats.iot.ap-northeast-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
          "topics": [
            ".../.../subscribeToVehicleLocation/..."
          ],
          "client": "..."
        }
      ],
      "newSubscriptions": {
        "subscribeToVehicleLocation": {
          "topic": ".../../subscribeToVehicleLocation/..",
          "expireTime": null
        }
      }
    }
  },
  "data": {
    "subscribeToVehicleLocation": null
  }
}

如果是这样,我们可以通过 API-网关(POST 方法)公开 Appsyn 端点吗?

为简单起见,我尝试在 API-Gateway 中使用 HTTP API

PS:

我以前可以使用原始 Appsync URL 订阅数据(在 Angular 7 中使用 AmplifyJS)。使用 API-网关 URL,我得到这个 WS 握手异常(使用 Amplify)。

WebSocket connection to 'wss://....execute-api.ap-northeast-1.amazonaws.com/graphql?header=...&payload=e30=' failed: Error during WebSocket handshake: Unexpected response code: 400

in AWSAppSyncRealTimeProvider.js:603 

更新 24-04-2020

我能够通过以下设置在 API-Gateway 中通过 AWS 服务调用来调用 Appsync。 (使用 API 网关提供的 REST 协议)

但是,我在 Amplify 中遇到 Web Socket 错误

API-网关配置

注意:AWS子域,是Appsync API端点的子域部分。

IAM 角色的信任关系

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "appsync.amazonaws.com",
          "apigateway.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

IAM 角色权限

我不认为你能像你想象的那样建立联系。

在 AppSync 中,查询和变更是使用普通 HTTP 连接 (REST) 传送的。另一方面,订阅是基于 websockets 的。这两种协议都是基于 TCP 的,但它们在服务器和客户端(浏览器和 sdk,如 amplify)中的处理方式不同。

AWS API Gateway supports WebSockets,但使用不同的配置。

您告诉我们,为了简单起见,您将 API 网关配置为使用 HTTP API。在这种情况下,它将无法工作,因为您在 API 网关中创建的端点未准备好与客户端执行 websocket 握手 (Amplify)。

要接受和控制握手,您必须将 API 创建为 Websocket API。但问题是:使用此 API 您不能只将呼叫转发给 AppSync。您将需要部署一个组件来实现 API Gateway WebSocket Implementation 所期望的连接控制。

Lambda 函数将是第一个想法,但随后出现了问题:您将如何控制和保持从 Lambda 到 AppSync 的 websocket 连接 API。您不能指望 Lambda 能做到这一点。

我可以想象以下实现适用于您的案例(但我认为这不是一个好的实现):

  1. 在 AWS API 网关
  2. 中实施 WebSocket API
  3. 部署一个组件来控制 WebSocket API 后端(这必须在实例或容器内)
  4. 从该组件,您可以使用 amplify 连接到 AppSync 中的 websocket 端点。
  5. 在该组件中,每次您从 AppSync 收到对特定 websocket 连接的请求时,您都会为 WebSocket API 调用 AWS API 网关回调 url 并转发响应。

总而言之,使用此解决方案,除了让基础设施的另一部分提供 AppSync 已经提供的开箱即用管道外,您还需要重现 AppSync 开箱即用提供的连接控制.

征求专家意见后,我终于放弃了通过API-Gateway曝光Appsync的计划。

Appsync & API-Gateway 在 AWS Stack 中属于同一层次结构。通过 API-Gateway 公开 Appsync 不是一个好主意,因为 Appsync 端点仍然是 public(导致后门)。

以下是我针对

的解决方案(仅考虑 Appsync
  1. 货币化范围:收集 Appsync metrics/trace 日志并根据 Cognito UserId 或 Apikey 计算 API 使用情况。

  2. 用法Plan/Quota:设置一个Lambda Datasource(在管道解析器中)增加Redis缓存中的命中计数(使用键作为 Apikey 和值作为具有自定义 TTL 的命中计数,例如 1 天)。

如果有更好的解决方案,欢迎分享。