您的服务器环境和 FCM

Firebase Cloud Messaging 的服务器端包含两个组件:

  • 由 Google 提供的 FCM 后端
  • 您的应用服务器或运行服务器逻辑的其他可信服务器环境,例如 Cloud Functions for Firebase 或由 Google 管理的其他云环境。

应用服务器或受信任的服务器环境向 FCM 后端发送消息请求,然后 FCM 后端再将消息发送到用户设备上运行的客户端应用。

受信任的服务器环境的要求

您的应用服务器环境必须符合以下条件:

  • 能够向 FCM 后端发送格式正确的消息请求。
  • 能够使用指数退避算法处理请求和重新发送请求。
  • 能够安全地存储服务器授权凭据和客户端注册令牌。
  • 对于 XMPP 协议(如果使用),服务器必须能够生成消息 ID 来唯一标识它发送的每条消息(FCM HTTP 后端会生成消息 ID 并在响应时返回这些 ID)。XMPP 消息 ID 对于每个发送者 ID 而言都应是唯一的。

选择服务器选项

您需要决定与 FCM 服务器进行交互的方式:使用 Firebase Admin SDK 或使用原始协议。 建议使用 Firebase Admin SDK,因为它支持各种流行的编程语言,而且处理身份验证和授权的方法非常便捷。

与 FCM 服务器交互的选项包括:

  • Firebase Admin SDK,支持 NodeJavaPythonC# 以及 Go
  • FCM HTTP v1 API 是最新的协议选项,具有更安全的授权功能和灵活的跨平台消息传递功能(Firebase Admin SDK 基于此协议并提供其所有固有优势)。由于新功能通常仅添加到 HTTP v1 API,因此我们建议在大多数用例中使用此 API。
  • 旧版 HTTP 协议。强烈建议新项目采用 FCM v1 HTTP API 而非旧版协议。
  • 旧版 XMPP 服务器协议。强烈建议新项目采用 FCM v1 HTTP API 而非旧版协议。

Firebase Admin SDK for FCM

Admin FCM API 可处理后端身份验证工作,同时便于发送消息和管理主题订阅。使用 Firebase Admin SDK,您可以执行以下操作:

  • 向单个设备发送消息
  • 向主题和与一个或多个主题匹配的条件语句发送消息。
  • 为设备订阅和退订主题
  • 针对不同目标平台构建量身定制的消息载荷。

Admin Node.js SDK 提供了用于向设备组发送消息的方法。

如需设置 Firebase Admin SDK,请参阅将 Firebase Admin SDK 添加到您的服务器。如果您已有 Firebase 项目,请从添加 SDK 开始。此外,请务必在项目的 Cloud Messaging 设置页面中启用 Cloud Messaging API。然后,安装 Firebase Admin SDK 后,您就可以开始将逻辑写入构建发送请求 (send request)。

FCM 服务器协议

目前 FCM 提供以下原始服务器协议:

您的应用服务器可以分开使用或同时使用这些协议。因为在向多个平台发送消息方面,FCM HTTP v1 API 是较新、较灵活的协议,因此推荐尽可能使用此协议。如果您需要从设备到服务器的上行消息传递功能,则需实现 XMPP 协议。

XMPP 消息传递与 HTTP 消息传递具有以下差异:

  • 上行/下行消息
    • HTTP:仅下行,即云到设备。
    • XMPP:上行和下行(即设备到云、云到设备)。
  • 消息传递(同步或异步)
    • HTTP:同步。应用服务器以 HTTP POST 请求的形式发送消息,并等待响应。此机制是同步的,且发送者无法在收到响应前发送其他消息。
    • XMPP:异步。应用服务器通过持续型 XMPP 连接,以全线速向/从所有设备发送/接收消息。 XMPP 连接服务器异步发送确认或失败通知(以 ACK 和 NACK JSON 编码的特殊 XMPP 消息形式)。
  • JSON
    • HTTP:JSON 消息以 HTTP POST 的形式发送。
    • XMPP:JSON 消息封装在 XMPP 消息中。
  • 纯文本
    • HTTP:纯文本消息以 HTTP POST 的形式发送。
    • XMPP:不支持。
  • 向多个注册令牌发送多播下行消息。
    • HTTP:支持 JSON 格式的消息。
    • XMPP:不支持。

实现 HTTP 服务器协议

如需发送消息,应用服务器需发出包含 HTTP 标头和 HTTP 正文(由 JSON 键值对组成)的 POST 请求。 如需详细了解标头和正文选项,请参阅构建应用服务器发送请求

实现 XMPP 服务器协议

FCM 消息的 JSON 载荷与 HTTP 协议类似,不同之处在于:

  • 不支持多个接收者。
  • FCM 会添加必填字段 message_id。此 ID 可唯一标识某个 XMPP 连接中的消息。来自 FCM 的 ACK 或 NACK 使用 message_id 来标识从应用服务器发送至 FCM 的消息。 因此,此 message_id 不仅必须是唯一的(按发送者 ID),而且始终存在。
  • XMPP 使用服务器密钥来向接入 FCM 的持久性连接提供授权。如需了解详情,请参阅向发送请求提供授权

除常规 FCM 消息外,还会发送控制消息,通过 JSON 对象中的 message_type 字段来表示。该值可以是“ack”“nack”或“control”(参见以下格式)。您的服务器可忽略任何未知 message_type 的 FCM 消息。

对于从 FCM 收到的每条设备消息,您的应用服务器都需要发送一条 ACK 消息。 不过,它始终无需发送 NACK 消息。如果您没有针对某条消息发送 ACK,那么 FCM 将在下次建立新的 XMPP 连接时重新发送该消息(除非该消息在那之前已过期)。

对于每一条从服务器发送至设备的消息,FCM 还将发送一条 ACK 或 NACK 消息。如果您未收到任何一种消息,则表示 TCP 连接在操作期间关闭,您的服务器需要重新发送消息。如需了解详情,请参阅流控制

如需查看所有消息参数的列表,请参阅协议参考

请求格式

包含有效载荷的消息 - 通知消息

以下是一个用于通知消息的 XMPP 节 (stanza):

<message id="">
  <gcm xmlns="google:mobile:data">
  {
     "to":"REGISTRATION_ID",  // "to" replaces "registration_ids"
     "notification": {
        "title": "Portugal vs. Denmark”,
        "body”: "5 to 1”
      },
      "time_to_live":"600"
  }
  </gcm>
</message>

包含有效载荷的消息 - 数据消息

以下是一个含有从应用服务器发送至 FCM 的 JSON 消息的 XMPP 节:

<message id="">
  <gcm xmlns="google:mobile:data">
  {
      "to":"REGISTRATION_ID",  // "to" replaces "registration_ids"
      "message_id":"m-1366082849205" // new required field
      "data":
      {
          "hello":"world",
      }
      "time_to_live":"600",
  }
  </gcm>
</message>

响应格式

FCM 响应有三种可能的形式。第一种是常规的“ACK”消息。但是,如果响应中包含错误,消息可能有 2 种不同形式,如下所述。

ACK 消息

以下是一个包含从 FCM 发送至应用服务器的 ACK/NACK 消息的 XMPP 节:

<message id="">
  <gcm xmlns="google:mobile:data">
  {
      "from":"REGID",
      "message_id":"m-1366082849205"
      "message_type":"ack"
  }
  </gcm>
</message>

NACK 消息

NACK 错误是一种常规的 XMPP 消息,其中 message_type 的状态消息为“nack”。 NACK 消息包含:

  • Nack 错误代码。
  • Nack 错误描述。

举例如下。

错误注册:

<message>
  <gcm xmlns="google:mobile:data">
  {
    "message_type":"nack",
    "message_id":"msgId1",
    "from":"SomeInvalidRegistrationToken",
    "error":"BAD_REGISTRATION",
    "error_description":"Invalid token on 'to' field: SomeInvalidRegistrationId"
  }
  </gcm>
</message>

无效 JSON:

<message>
 <gcm xmlns="google:mobile:data">
 {
   "message_type":"nack",
   "message_id":"msgId1",
   "from":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
   "error":"INVALID_JSON",
   "error_description":"InvalidJson: JSON_TYPE_ERROR : Field \"time_to_live\" must be a JSON java.lang.Number: abc"
 }
 </gcm>
</message>

超出设备消息率:

<message id="...">
  <gcm xmlns="google:mobile:data">
  {
    "message_type":"nack",
    "message_id":"msgId1",
    "from":"REGID",
    "error":"DEVICE_MESSAGE_RATE_EXCEEDED",
    "error_description":"Downstream message rate exceeded for this registration id"
  }
  </gcm>
</message>

如需 NACK 错误代码的完整列表,请参阅服务器参考。除非另有说明,否则不能重试 NACK 消息。异常 NACK 错误代码的处理方式应与 INTERNAL_SERVER_ERROR 相同。

节错误

在某些情况下,您还可能遇到节错误。 节错误包含:

  • 节错误代码。
  • 节错误描述(自由文本)。

例如:

<message id="3" type="error" to="123456789@fcm.googleapis.com/ABC">
  <gcm xmlns="google:mobile:data">
     {"random": "text"}
  </gcm>
  <error code="400" type="modify">
    <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
    <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
      InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n
    </text>
  </error>
</message>

控制消息

FCM 需要不时关闭连接以执行负载均衡。FCM 在关闭连接之前会发送一条 CONNECTION_DRAINING 消息,表示连接正在被排空,很快将会关闭。“排空”是指阻止消息流进入某个连接,但允许已进入流水线 的所有消息继续运行。当您收到 CONNECTION_DRAINING 消息时,应立即开始向另一个 FCM 连接发送消息,还可根据需要开启一个新的连接。但是,您应该让原来的连接保持开启状态,并继续接收可能来自该连接的消息(并对其进行确认 ACK)。FCM 会在准备就绪后关闭连接。

CONNECTION_DRAINING 消息如下所示:

<message>
  <data:gcm xmlns:data="google:mobile:data">
  {
    "message_type":"control"
    "control_type":"CONNECTION_DRAINING"
  }
  </data:gcm>
</message>

目前 CONNECTION_DRAINING 是唯一受支持的 control_type

流控制

发送至 FCM 的每一条消息都会收到 ACK 或 NACK 响应。未收到其中任何一种响应的消息被视为待处理。如果待处理的消息数量达到 100 条,应用服务器应当停止发送新的消息,等待 FCM 对某些现有的待处理消息进行确认,如图 1 所示:

FCM 与应用服务器之间的控制流的详细图示

图 1. 消息/确认 (ack) 流。

反之而言,为了避免应用服务器过载,如果有太多未确认的消息,FCM 也将停止发送上行消息。因此,应用服务器应尽快“ACK”(确认)通过 FCM 收到的来自客户端应用的上行消息,以便维持持续的传入消息流。上述待处理消息限制不适用于这类 ACK。即使待处理消息数量达到 100 条,应用服务器也应继续为从 FCM 收到的消息发送 ACK,以避免阻塞新的上行消息的传递。

ACK 仅在同一个连接的上下文环境中有效。如果连接在消息确认 (ACK) 前关闭了,应用服务器应等待 FCM 重新发送该上行消息,然后再次对其进行确认。同样地,所有未能在连接关闭之前从 FCM 收到 ACK/NACK 的待处理消息都应重新发送。