サーバー環境と FCM

サーバー側の Firebase Cloud Messaging は、次の 2 つのコンポーネントで構成されます。

  • Google が提供する FCM バックエンド
  • サーバー側ロジックを実行するアプリサーバーやその他の信頼できるサーバー環境(Google によって管理される Cloud Functions for Firebase やその他のクラウド環境など)。

アプリサーバーや信頼できるサーバー環境は、FCM バックエンドにメッセージ リクエストを送信し、FCM バックエンドからユーザーのデバイスで実行されているクライアント アプリにメッセージがルーティングされます。

信頼できるサーバー環境の要件

アプリサーバー環境は次の条件を満たしている必要があります。

  • 適切にフォーマットされたメッセージ リクエストを FCM バックエンドに送信できること。
  • リクエストを処理し、指数バックオフを使用して再送信できること。
  • サーバーの認証情報とクライアント登録トークンを安全に保存できること。
  • XMPP プロトコルを使用する場合は、送信する各メッセージを一意に識別するメッセージ ID をサーバーで生成できること(FCM HTTP バックエンドはメッセージ ID を生成し、それらをレスポンスで返します)。XMPP メッセージ ID は送信者 ID ごとに一意のものにする必要があります。

サーバー オプションの選択

FCM サーバーとの対話には Firebase Admin SDK または基本的なプロトコルの 2 つの方法があり、どちらを使用するか決定する必要があります。一般的なプログラミング言語で広くサポートされていること、認証や認可を簡単に処理できることから、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 を使用することを強くおすすめします。

FCM 用 Firebase Admin SDK

Admin FCM API は、バックエンドによる認証を処理し、メッセージの送信とトピック登録の管理を容易にします。Firebase Admin SDK を使用して、次の処理を行うことができます。

  • 個々のデバイスにメッセージを送信する
  • トピックや 1 つ以上のトピックに一致する条件文にメッセージを送信する
  • トピックに対してデバイスの登録と登録解除を行う
  • ターゲット プラットフォームの種類に合わせたメッセージ ペイロードを作成する

Admin Node.js SDK には、デバイス グループにメッセージを送信するための方法が用意されています。

Firebase Admin SDK を設定するには、サーバーに Firebase Admin SDK を追加するをご覧ください。Firebase プロジェクトがすでに存在する場合は、まず SDK の追加を行ってください。また、プロジェクトの Cloud Messaging の設定ページで、Cloud Messaging API を有効にしておいてください。そのうえで Firebase Admin SDK をインストールすると、送信リクエストを作成するためのロジックの記述を開始できます。

FCM サーバー プロトコル

現在のところ、FCM は次の未加工サーバー プロトコルを提供しています。

アプリサーバーでは、これらのプロトコルを別々に、または連携して使用できます。複数のプラットフォームにメッセージを送信するうえで最も柔軟性の高い最新のものは FCM HTTP v1 API であり、可能な限りそれを使用するようおすすめします。デバイスからサーバーへのアップストリーム メッセージングが要件に含まれている場合は、XMPP プロトコルを実装する必要があります。

XMPP メッセージングは、次の点で HTTP メッセージングとは異なります。

  • アップストリームまたはダウンストリーム メッセージ
    • HTTP: ダウンストリームのみ、クラウドからデバイス。
    • XMPP: アップストリームとダウンストリーム(デバイスからクラウド、クラウドからデバイス)。
  • メッセージング(同期または非同期)
    • HTTP: 同期。アプリサーバーは、HTTP POST リクエストとしてメッセージを送信し、レスポンスを待機します。このメカニズムは同期的であり、送信者はレスポンスを受信するまで別のメッセージを送信できません。
    • XMPP: 非同期。アプリサーバーはすべてのデバイスとの間で、永続的な XMPP 接続を使用して最大の回線速度でメッセージを送受信します。XMPP 接続サーバーは、確認応答または失敗応答の通知(特別な ACK および NACK JSON エンコード XMPP メッセージの形式)を非同期で送信します。
  • JSON
    • HTTP: HTTP POST として送信された JSON メッセージ。
    • XMPP: XMPP メッセージにカプセル化された JSON メッセージ。
  • 書式なしテキスト
    • HTTP: HTTP POST として送信された書式なしテキスト メッセージ。
    • XMPP: サポートされていません。
  • 複数の登録トークンに送信されるマルチキャスト ダウンストリーム。
    • HTTP: JSON メッセージ形式でサポートされます。
    • XMPP: サポートされていません。

HTTP サーバー プロトコルの実装

メッセージを送信するために、アプリサーバーは HTTP ヘッダーと、JSON Key-Value ペアで構成される HTTP 本文とを含む 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 を送信しないと、そのメッセージは、先に期限切れにならない限り、新しい XMPP 接続が次回確立されたときに FCM から再送信されます。

FCM はさらに、サーバーからデバイスへのメッセージごとに、ACK または NACK を送信します。どちらも受信しないときは、オペレーション中に TCP 接続が閉じられたことを意味し、サーバーはメッセージを再送信する必要があります。詳細については、フロー制御をご覧ください。

すべてのメッセージ パラメータの一覧については、プロトコル リファレンスをご覧ください。

リクエストの形式

ペイロードを含むメッセージ - 通知メッセージ

次に、通知メッセージの XMPP スタンザを示します。

<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 レスポンスが取り得る形式には 3 つあります。最初の形式は通常の「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 エラーは、message_type ステータス メッセージが「nack」である通常の XMPP メッセージです。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 に達すると、アプリサーバーは、図 1 に示したように、新しいメッセージの送信を停止し、既存の保留中のメッセージを FCM が確認するまで待機する必要があります。

FCM とアプリサーバーの間の制御フローの詳細図

図 1: メッセージと ACK のフロー

反対に、アプリサーバー側で未確認のメッセージ数が非常に多い場合、アプリサーバーに負荷をかけすぎないように FCM はメッセージの送信を停止します。したがって、アプリサーバーは着信メッセージのフローを一定に保つため、FCM 経由でクライアント アプリケーションから受信したアップストリーム メッセージにできるだけ早く「ACK」で応答する必要があります。上記の保留中のメッセージ数の制限は、これらの ACK には適用されません。FCM で保留中のメッセージ数が 100 に達した場合でも、アプリサーバーは、新しいアップストリーム メッセージの配信が妨げられないように、FCM から受信したメッセージに対して ACK を送信し続ける必要があります。

ACK は、1 つの接続のコンテキスト内でのみ有効です。メッセージに対して ACK で応答する前に接続が閉じた場合、アプリサーバーは、FCM がアップストリーム メッセージを再送信するまで待機してから、再度 ACK を送信する必要があります。同様に、接続が閉じられる前に FCM から ACK または NACK を受信しなかった保留中のすべてのメッセージを再度送信する必要があります。