Firebase Cloud Messaging服务器端包含两个组件:
- GCM 连接服务器,由 Google 提供。这些服务器从一个应用服务器获取消息,并将其发送至在设备上运行的客户端应用。Google 为 HTTP 和 XMPP 提供连接服务器。
- 一台应用服务器,您必须在您的环境中实现它。此应用服务器通过选定的FCM连接服务器,使用合适的 XMPP 或 HTTP 协议向客户端应用发送数据。
完整的 FCM 实现既需要客户端实现,也需要服务器实现。有关如何实现客户端应用的详细信息,请参阅适合您的平台(iOS、Android 或 Chrome)的客户端指南。
应用服务器的作用
在您可以编写使用 Firebase Cloud Messaging的客户端应用之前,您必须拥有一个满足以下标准的应用服务器:
- 能够与您的客户端通信。
- 能够向FCM连接服务器发送格式正确的请求。
- 能够使用指数回退方式处理请求和重新发送请求。
- 能够存储服务器密钥和客户端注册令牌。注:绝对不要在任何客户端代码中包含服务器密钥。
- 对于 XMPP,服务器必须能够生成消息 ID 来唯一地标识它发送的每条消息(GCM HTTP 服务器生成消息 ID 并在响应中返回相应 ID)。XMPP 消息 ID 对于每个发送者 ID 应唯一。
您需要决定使用哪个FCM连接服务器协议来让您的应用服务器与FCM连接服务器交互。请注意,如果您想从客户端应用使用上游消息传递,则必须使用 XMPP。有关更多详细讨论,请参阅选择FCM连接服务器协议。
选择FCM连接服务器协议
目前,FCM提供两种连接服务器协议:HTTP 和 XMPP。您的应用服务器可以单独或以串联方式使用这些协议。XMPP 消息传递与 HTTP 消息传递具有以下差异:
- 上游/下游消息
- HTTP:仅适用于云端至设备、最多 4KB 数据的下游消息。
- XMPP:适用于上游和下游(设备至云端、云端至设备)、最多 4 KB 数据的消息。
- 消息传递(同步或异步)
- 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 连接服务器协议
本部分将介绍FCM HTTP 连接服务器。连接服务器是一种由 Google 提供的服务器,它可以从应用服务器接收消息并将其发送至设备。
有关所有消息参数以及可为其提供支持的连接服务器的列表,请参阅服务器参考。
身份验证
要发送消息,应用服务器需发出 POST 请求。例如:
https://fcm.googleapis.com/fcm/send
某个消息请求由两部分组成:HTTP 标头和 HTTP 正文。
HTTP 标头必须包含以下标头:
Authorization: key=YOUR_SERVER_KEYContent-Type:application/json(JSON 格式);application/x-www-form-urlencoded;charset=UTF-8(纯文本格式)。 如果省略Content-Type,即视为纯文本格式。
例如:
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data" : {
...
},
}
HTTP 正文内容取决于您使用的是 JSON 还是纯文本。有关 JSON 或纯文本消息中可能包含的所有参数列表,请参阅服务器参考。
检查服务器密钥的有效性
如果您在发送消息时收到身份验证错误,请检查服务器密钥的有效性。例如,在 Android 上运行以下命令:
# api_key=YOUR_SERVER_KEY
# curl --header "Authorization: key=$api_key" \
--header Content-Type:"application/json" \
https://fcm.googleapis.com/fcm/send \
-d "{\"registration_ids\":[\"ABC\"]}"
如果您收到 401 HTTP 状态代码,则表示您的服务器密钥无效。或者,您应看到类似于如下内容:
{"multicast_id":6782339717028231855,"success":0,"failure":1,
"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
如果您想要确认注册令牌的有效性,可以将"ABC"替换为注册标记。
请求格式
本部分介绍如何格式化 JSON 和纯文本请求。有关请求中可包含字段的完整列表,请参阅服务器参考。
发送至同步
以下是使用 JSON 格式的最小可能请求(不带有任何参数且仅有一个接收者的消息):
{ "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..." }
以下是使用纯文本格式的同一个示例:
registration_id=42
带有负载的消息 — 通知消息
以下是一个通知消息:
{ "notification": {您可以同时有通知消息和数据负载消息。请参阅 服务器参考以了解通知负载所支持的密钥。有关通知消息和数据消息选项的详细信息,请参阅 消息负载中的通知和数据。
"title": "Portugal vs. Denmark",
"text": "5 to 1"
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
带有负载的消息 — 数据消息
以下是带有数据负载的消息:
{ "data": {
"score": "5x1",
"time": "15:10"
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
以下是设置所有可选字段的消息:
{ "collapse_key": "score_update",
"time_to_live": 108,
"delay_while_idle": true,
"data": {
"score": "4x8",
"time": "15:16.2342"
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
以下是使用纯文本格式的同一条消息:
collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.score=4x8&data.time=15:16.2342®istration_id=42如果您的企业设有防火墙以限制向互联网发送或从其接收的流量,则需要将其配置为允许连接FCM才能让您的 Firebase Cloud Messaging客户端应用接收消息。要打开的端口为:5228、5229 和 5230。FCM通常仅使用 5228,但有时也会使用 5229 和 5230。FCM不提供具体的 IP,所以您的防火墙应设置为允许外部连接至 Google 15169 ASN 的封锁 IP 列表中包含的所有 IP 地址。
响应格式
尝试发送消息时可能有两种结果:
- 消息处理成功。HTTP 响应有一个 200 状态,正文包含消息状态的详细信息。
- FCM服务器拒绝请求。HTTP 响应包含一个非 200 状态代码(例如 400、401 或 5xx)。
当 JSON 请求成功后(HTTP 状态代码 200),返回的 JSON 对象包含 下游 HTTP 消息响应正文。记录所有消息的消息 ID 值,方便您通过 Google 支持或 GCM 诊断排除消息故障。
如果 failure 和 canonical_ids 的值是 0,则无需分析响应的其余部分。否则,我们建议您遍历结果字段,并对该列表中的每个对象执行以下操作:
- 如果设置了
message_id,则检查registration_id:- 如果设置了
registration_id,则将服务器数据库中的原始 ID 替换为新值(规范 ID)。请注意,原始 ID 并非结果的一部分,因此您需要从请求中传送的registration_ids列表中获取该 ID(使用相同索引)。
- 如果设置了
- 否则,应获取
error的值:- 如果值是
Unavailable,您应重试在另一个请求中发送。 - 如果值是
NotRegistered,您应从服务器数据库中删除注册 ID,因为应用已从设备中卸载,或客户端应用未配置为接收消息。 - 否则,请求中传送的注册令牌将发生错误;这可能是一个不可恢复的错误,需要从服务器数据库中删除注册。请参阅下游消息错误响应代码以了解所有可能的错误值。
- 如果值是
如果纯文本请求成功(HTTP 状态代码 200),响应正文将包含 1 或 2 行键值对。第一行始终存在,其内容是 id=ID of sent message 或 Error=GCM error code。如果有第二行,其格式为 registration_id=canonical ID。第二行是可选项,仅在第一行不是错误时才能发送第二行。建议采用 JSON 响应的类似处理方式来处理纯文本响应:
- 如果第一行以
id开头,则检查第二行:- 如果第二行以
registration_id开头,则获取其值,并替换服务器数据库中的注册令牌。
- 如果第二行以
- 否则,应获取
Error的值:- 如果值为
NotRegistered,则从服务器数据库中删除注册令牌。 - 否则,可能会发生不可恢复的错误(注意:纯文本请求永远不会返回错误代码
Unavailable,而是应返回 500 HTTP 状态)。
- 如果值为
响应示例
本部分介绍几个表示消息处理成功的响应示例。请参阅请求格式以了解这些响应所依据的请求。
以下是一个简单的 JSON 消息示例,该消息成功发送至一个接收者,且响应中不含规范 ID:
{ "multicast_id": 108,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{ "message_id": "1:08" }
]
}
或者,如果请求是纯文本格式:
id=1:08
以下是向 6 个接收者发送 JSON 消息的结果(ID 分别是 4、8、15、16、23 和 42),其中 3 条消息处理成功,返回 1 个规范注册令牌,还有 3 个错误:
{ "multicast_id": 216,
"success": 3,
"failure": 3,
"canonical_ids": 1,
"results": [
{ "message_id": "1:0408" },
{ "error": "Unavailable" },
{ "error": "InvalidRegistration" },
{ "message_id": "1:1516" },
{ "message_id": "1:2342", "registration_id": "32" },
{ "error": "NotRegistered"}
]
}
在本例中:
- 第一条消息:成功,不需要。
- 第二条消息:应重新发送(至注册令牌 8)。
- 第三条消息:发生不可恢复错误(可能是数据库中的值已损坏)。
- 第四条消息:成功,不需做任何处理。
- 第五条消息:成功,但应更新服务器数据库中的注册令牌(从 23 更新为 32)。
- 第六条消息:应从服务器数据库中删除注册令牌 (42),因为应用已从设备中卸载。
如果仅有上面的第三条消息是以纯文本格式发送:
Error=InvalidRegistration
如果上面的第五条消息也是以纯文本格式发送:
id=1:2342
registration_id=32
实现 XMPP 连接服务器协议
Cloud Connection Server (CCS) 是一个 XMPP 终点,可向 Google 服务器提供持续的异步双向连接。连接可用于在服务器与已连接FCM的用户设备之间发送和接收消息。
您可以继续使用 HTTP 请求机制向与使用 XMPP 的 CSS 并行的 Firebase Cloud Messaging服务器发送消息。CCS 的一些优点包括:
- XMPP 的异步特性让您能够用更少的资源发送更多消息。
- 通信是双向的—不仅能让您的服务器向设备发送消息,还能让设备将消息返回您的服务器。
- 设备可利用接收消息所使用的相同连接返回消息,从而延长电池寿命。
有关所有消息参数以及可为其提供支持的连接服务器的列表,请参阅服务器参考。
建立连接
CCS 仅使用 XMPP 作为已认证传输层,因此您可使用大多数 XMPP 库来管理连接。
要优化 XMPP 连接服务器的可靠性和双向消息传递能力,需要创建长期连接,而不要频繁地断开连接和重新连接。 CCS XMPP 终点运行于 fcm-xmpp.googleapis.com:5235。进行功能测试时(针对非生产用户),您应改为连接至fcm-xmpp.googleapis.com:5236(注意端口不同)。在预生产环境(运行最新 CCS 的较小型环境)中进行常规测试将有助于隔离真实用户与测试代码,以及提早检测出异常行为更改。请注意,连接将收到发往FCM发送者 ID 的上游消息,无论其连接到哪种环境。因此,连接至 fcm-xmpp.googleapis.com:5236 的测试代码应使用不同的FCM发送者 ID,以避免通过测试连接发送来自生产通信的上游消息。
连接需要符合两个重要条件:
- 您必须启用传输层安全 (TLS) 连接。请注意,CCS 目前不支持 STARTTLS 扩展。
- CCS 需要一个使用
<your_GCM_Sender_Id>@gcm.googleapis.com(GCM 发送者 ID)和服务器密钥作为密码的 SASL PLAIN 身份验证机制,其中发送者 ID 和服务器密钥是您在配置客户端应用时获得的值。请参阅您的平台的客户端文档以了解关于获得这些凭据的信息。
如有任何点连接失败,您应立即重新连接。如果身份验证后连接断开,无需后退。
对于每个发送者 ID,FCM允许 1000 个并行连接。
身份验证
以下代码段说明如何在 CCS 中进行身份验证。
客户端
<stream:stream to="gcm.googleapis.com"
version="1.0" xmlns="jabber:client"
xmlns:stream="http://etherx.jabber.org/streams">
服务器
<str:features xmlns:str="http://etherx.jabber.org/streams"> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanism>X-OAUTH2</mechanism> <mechanism>X-GOOGLE-TOKEN</mechanism> <mechanism>PLAIN</mechanism> </mechanisms> </str:features>
客户端
<auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">MTI2MjAwMzQ3OTMzQHByb2plY3RzLmdjbS5hb mFTeUIzcmNaTmtmbnFLZEZiOW1oekNCaVlwT1JEQTJKV1d0dw==</auth>
服务器
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
下游消息
建立 XMPP 连接后,CCS 和您的服务器即可使用标准 XMPP <message> 节来回发送以 JSON 编码的消息。<message>正文必须为:
<gcm xmlns:google:mobile:data>
JSON payload
</gcm>
常规FCM消息的 JSON 负载类似于FCM http 终点使用的负载,不同之处在于:
- 不支持多个接收者。
- 使用
registration_ids代替to。 - CCS 添加字段
message_id,此为必填字段。此 ID 唯一标识 XMPP 连接中的消息。来自 CCS 的 ACK 或 NACK 使用message_id标识从应用服务器发送至 CCS 的消息。因此,应注意,此message_id并非唯一(对于每个发送者 ID),但始终存在。
除了常规FCM消息外,还会发送控制消息,此消息由 JSON 对象中的 message_type 字段表示。该值可以是"ack"或"nack",或"control"(参见以下格式)。您的服务器可能会忽略任何带有未知 message_type 的FCM消息。
对于您的应用服务器从 CCS 收到的每条设备消息,它均需要发送一条 ACK 消息。无需发送 NACK 消息。如果您不为消息发送 ACK,CCS 将在下次建立新的 XMPP 连接时重新发送,除非该消息已过期。
CCS 还将对每一条服务器至设备消息发送一条 ACK 或 NACK 消息。如果您未收到任何一种消息,则表示 TCP 连接在运行过程中关闭,您的服务器需要重新发送消息。有关详情,请参见流控制。
有关所有消息参数以及可为其提供支持的连接服务器的列表,请参阅服务器参考。
请求格式
发送至同步
以下是一个用于"发送至同步"消息的简单的 XMPP 节:
<message id="">
<gcm xmlns="google:mobile:data">
{
"to":"REGISTRATION_ID", // "to" replaces "registration_ids"
}
</gcm>
</message>
带有负载的消息 — 通知消息
以下是一个用于通知消息的 XMPP 节:
<message id="">
<gcm xmlns="google:mobile:data">
{
"to":"REGISTRATION_ID", // "to" replaces "registration_ids"
"notification": {
"title": "Portugal vs. Denmark”,
"text”: "5 to 1”
},
"time_to_live":"600"
}
}
</gcm>
</message>
您可以同时有通知消息和数据负载消息。请参阅
服务器参考以了解通知负载所支持的密钥。另请参阅
消息负载中的通知和数据。
带有负载的消息 — 数据消息
以下是一个含有从应用服务器发送至 CCS 的 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",
"delay_while_idle": true/false,
"delivery_receipt_requested": true/false
}
</gcm>
</message>
响应格式
CCS 响应可能有三种形式。第一种是常规"ack"消息。但是,如果响应中包含错误,消息可能有两种不同形式,如下所述。
ACK 消息
以下是一个包含从 CCS 发送至应用服务器的 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":"SomeInvalidRegistrationId",
"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@gcm.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>
控制消息
有时,CCS 需要关闭连接以执行负载平衡。在关闭连接之前,CCS 会发送一条 CONNECTION_DRAINING 消息,以说明连接即将被排空,很快会关闭。"排空"是指阻止消息流进入某连接,但允许已进入管道的消息继续运行。当您收到CONNECTION_DRAINING 消息时,应立即开始向另一个 CCS 连接发送消息,如有必要,可开启新的连接。但是,您应使原始连接保持开启状态,并继续接收可能来自该连接的消息(并对其进行确认 [ACK])— CCS 将在准备就绪后关闭连接。
CONNECTION_DRAINING 消息如下所示:
<message>
<data:gcm xmlns:data="google:mobile:data">
{
"message_type":"control"
"control_type":"CONNECTION_DRAINING"
}
</data:gcm>
</message>
CONNECTION_DRAINING 是当前唯一支持的 control_type。
接收送达回执
对于 Android 和 Chrome 客户端应用,当设备确认收到由 CCS 发送的消息后,您可获得送达回执(从 CCS 发送至您的应用服务器)。
发送至 iOS 设备的消息不支持送达回执。 要启用此功能,应用服务器发送至 CCS 的消息必须包含 delivery_receipt_requested 字段。如果该字段设置为true,则当设备确认收到某特定消息时,CCS 将发送送达回执。
以下是一个含有 JSON 消息的 XMPP 节,其中 "delivery_receipt_requested" 设置为 true:
<message id="">
<gcm xmlns="google:mobile:data">
{
"to":"REGISTRATION_ID",
"message_id":"m-1366082849205"
"data":
{
"hello":"world",
}
"time_to_live":"600",
"delay_while_idle": true,
"delivery_receipt_requested": true
}
</gcm>
</message>
以下是 CCS 发送的送达回执示例,用于告诉您的应用服务器设备已收到 CCS 向其发送的消息:
<message id="">
<gcm xmlns="google:mobile:data">
{
"category":"com.example.yourapp", // to know which app sent it
"data":
{
“message_status":"MESSAGE_SENT_TO_DEVICE",
“original_message_id”:”m-1366082849205”
“device_registration_id”: “REGISTRATION_ID”
},
"message_id":"dr2:m-1366082849205",
"message_type":"receipt",
"from":"gcm.googleapis.com"
}
</gcm>
</message>
请注意以下事项:
- 将
"message_type"设置为"receipt"。 - 将
"message_status"设置为"MESSAGE_SENT_TO_DEVICE",表示设备已收到消息。请注意,在该例中,"message_status"不是一个字段,而是数据负载的一部分。 - 回执消息 ID 包含原始消息 ID,但带有一个前缀
dr2:。应用服务器发送的 ACK 消息中必须含有此 ID,在本例中为dr2:m-1366082849205。 - 原始消息 ID、设备注册令牌以及状态均包含在
"data"字段中。 - 如果 CCS 和设备之间的连接不稳定,Firebase Cloud Messaging可能会发送多个重复的送达回执。您可以安全地忽略这些重复回执。
流控制
发送至 CCS 的每一条消息都会收到 ACK 或 NACK 响应。未收到任何响应的消息被视为挂起。如果挂起消息计数达到 100,应用服务器应停止发送新消息,等待 CCS 对某些现有挂起消息进行确认,如图 1 所示:
图 1. 消息/ack 流。
相反,为避免应用服务器过载,如果有太多未确认的消息,CCS 将停止发送。因此,应用服务器应尽快"ACK"(确认)通过 CCS 从客户端应用收到的上游消息,以便尽快保持稳定的进入消息流。上述挂起消息限制不适用于这些 ACK。即使挂起消息计数达到 100,应用服务器也应继续为从 CCS 收到的消息发送 ACK,以避免阻塞新的上游消息传递。
ACK 仅在一个连接环境中有效。如果连接在消息可获确认之前关闭,应用服务器应等待 CCS 重新发送上游消息,然后再次对其进行确认。同样地,所有在连接关闭之前未从 CCS 收到 ACK/NACK 的挂起消息都应重新发送。
这篇博客介绍了如何使用Firebase Cloud Messaging (FCM)进行服务器端推送,包括HTTP和XMPP连接服务器协议的实现、身份验证、消息格式和响应处理。内容涵盖了FCM的连接、消息类型、身份验证机制、消息优先级和生命周期等关键概念,为开发者提供了实现FCM推送服务的详细指导。
3685





