Tu entorno de servidor y FCM

Los servidores de Firebase Cloud Messaging tienen dos componentes:

  • El backend de FCM proporcionado por Google
  • Tu servidor de apps, o bien otro entorno de servidor de confianza, en el que se ejecuta la lógica del servidor, como Cloud Functions para Firebase o entornos de nube administrados por Google

El servidor de apps o el entorno de servidor de confianza envía solicitudes de mensajes al backend de FCM, que luego envía mensajes a las apps cliente que se ejecutan en los dispositivos de los usuarios.

Requisitos del entorno de servidor de confianza

El entorno del servidor de apps debe cumplir con los siguientes criterios:

  • Poder enviar solicitudes de mensajes con el formato correcto al backend de FCM
  • Poder administrar solicitudes y reenviarlas con una retirada exponencial
  • Poder almacenar las credenciales de autorización del servidor y los tokens de registro de cliente de forma segura
  • Si se usa el protocolo XMPP, el servidor debe generar ID de mensajes a fin de identificar exclusivamente cada mensaje que envía (el backend de HTTP de FCM genera ID de mensajes y los muestra en la respuesta); los ID de mensajes XMPP deben ser únicos por ID de remitente

Elige una opción de servidor

Deberás decidir de qué manera comunicarte con los servidores de FCM: a través del SDK de Firebase Admin o los protocolos sin procesar. El SDK de Firebase Admin es el método recomendado, ya que es compatible con lenguajes de programación populares y cuenta con métodos útiles para controlar la autenticación y la autorización.

Para comunicarte con los servidores de FCM, puedes usar las siguientes opciones:

  • El SDK de Firebase Admin, que es compatible con Node.js, Java, Python, C# y Go
  • La API de FCM HTTP v1, que es la opción de protocolo más actualizada y cuenta con una autorización más segura y funciones flexibles de mensajería multiplataforma (el SDK de Firebase Admin se basa en este protocolo y proporciona todas sus ventajas propias) Debido a que, por lo general, las funciones nuevas se agregan solo a la API de HTTP v1, recomendamos usar esta API para la mayoría de los casos de uso.
  • El protocolo HTTP heredado. Se recomienda que los proyectos nuevos adopten la API de HTTP versión 1 de FCM en lugar del protocolo heredado.
  • El protocolo del servidor XMPP heredado Se recomienda que los proyectos nuevos adopten la API de HTTP versión 1 de FCM en lugar del protocolo heredado.

SDK de Firebase Admin para FCM

La API de Admin de FCM se encarga de la autenticación con el backend y facilita el envío de mensajes y la administración de suscripciones a temas. Con el SDK de Firebase Admin, puedes realizar las siguientes operaciones:

  • Enviar mensajes a dispositivos individuales
  • Enviar mensajes a temas y declaraciones de estado que coincidan con uno o más temas
  • Suscribir dispositivos a temas y anular suscripciones a ellos
  • Generar cargas útiles de mensaje adaptadas a distintas plataformas de destino

El SDK de Admin de Node.js proporciona métodos para enviar mensajes a grupos de dispositivos.

Si quieres configurar el SDK de Firebase Admin, consulta Agrega el SDK de Firebase Admin a tu servidor. Si ya tienes un proyecto de Firebase, primero consulta Agrega el SDK. Además, asegúrate de habilitar la API de Cloud Messaging en la página de configuración de Cloud Messaging del proyecto. Una vez que instales el SDK de Firebase Admin, podrás comenzar a escribir la lógica para crear solicitudes de envío.

Protocolos de servidor FCM

Actualmente, FCM proporciona los siguientes protocolos de servidor sin procesar:

Tu servidor de apps puede usar estos protocolos de manera independiente o en conjunto. Te recomendamos usar la API de FCM HTTP v1 siempre que sea posible, ya que es la más actualizada y flexible para enviar mensajes a diversas plataformas. Si tus requisitos incluyen la mensajería ascendente desde los dispositivos al servidor, deberás implementar el protocolo XMPP.

La mensajería XMPP difiere de la mensajería HTTP de las siguientes formas:

  • Mensajes ascendentes/descendentes
    • HTTP: Solo descendentes, de la nube al dispositivo
    • XMPP: Ascendentes y descendentes (del dispositivo a la nube y de la nube al dispositivo)
  • Mensajería (síncrona o asíncrona)
    • HTTP: Síncrono. Los servidores de apps envían mensajes como solicitudes HTTP POST y esperan una respuesta. Este mecanismo es síncrono y bloquea al emisor para que no pueda enviar otro mensaje hasta que se reciba la respuesta.
    • XMPP: Asíncrono. Los servidores de apps envían y reciben mensajes hacia y desde todos los dispositivos a velocidad máxima a través de conexiones XMPP persistentes. El servidor de conexiones XMPP envía notificaciones de confirmación o de error (en la forma de mensajes XMPP ACK y NACK especiales codificados en JSON) de forma asíncrona.
  • JSON
    • HTTP: Mensajes JSON enviados como HTTP POST
    • XMPP: Mensajes JSON encapsulados en mensajes XMPP
  • Texto sin formato
    • HTTP: Mensajes de texto sin formato enviados como HTTP POST
    • XMPP: No compatible
  • Mensajes multidifusión descendentes enviados a diversos tokens de registro
    • HTTP: Compatible con el formato de mensajes JSON
    • XMPP: No compatible

Implementación del protocolo de servidor HTTP

Para enviar un mensaje, el servidor de apps envía una solicitud POST con un encabezado HTTP y un cuerpo HTTP compuesto por pares clave-valor JSON. Para ver detalles sobre las opciones de encabezado y cuerpo, consulta Compila solicitudes de envío del servidor de apps.

Implementación del protocolo del servidor XMPP

La carga útil JSON para mensajes FCM es similar al protocolo HTTP, con las siguientes excepciones:

  • No admite múltiples receptores.
  • FCM agrega el campo message_id, que es obligatorio. Este ID identifica exclusivamente el mensaje en una conexión XMPP. El ACK o NACK de FCM usa el message_id para identificar un mensaje enviado desde los servidores de apps a FCM. Por lo tanto, es importante que este message_id no solo sea único (por ID de remitente), sino que siempre esté presente.
  • XMPP usa la clave de servidor para autorizar una conexión continua a FCM. Consulta Autoriza solicitudes de envío para obtener más información.

Además de los mensajes habituales de FCM, se envían mensajes de control indicados por el campo message_type en el objeto JSON. El valor puede ser “ack” o “nack”, o “control” (consulta los formatos a continuación). Tu servidor puede ignorar cualquier mensaje de FCM con un valor desconocido de message_type.

Por cada mensaje de dispositivo que tu servidor de apps recibe de FCM, el servidor debe enviar un mensaje ACK. Nunca es necesario que envíe un mensaje NACK. Si no envías un ACK para un mensaje, FCM lo reenvía la próxima vez que se establece una nueva conexión XMPP, a menos que el mensaje caduque antes.

FCM también envía un ACK o NACK para cada mensaje de servidor a dispositivo. Si no recibes ninguno, significa que la conexión TCP se cerró durante la operación y tu servidor debe reenviar los mensajes. Consulta Control de flujo para obtener información detallada.

Consulta la Referencia del protocolo para ver una lista de todos los parámetros de mensajes.

Formato de la solicitud

Mensaje con carga útil: Mensaje de notificación

La siguiente es una estrofa XMPP para un mensaje de notificación:

<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>

Mensaje con carga útil: Mensaje de datos

La siguiente es una estrofa XMPP que contiene el mensaje JSON de un servidor de apps a FCM:

<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>

Formato de la respuesta

Una respuesta de FCM puede tener tres formatos posibles. El primero es un mensaje "ack" normal. Sin embargo, cuando la respuesta contiene un error, el mensaje puede asumir dos formatos diferentes, que se describen a continuación.

Mensaje ACK

La siguiente es una estrofa XMPP que contiene el mensaje ACK/NACK de FCM al servidor de apps:

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

Mensaje NACK

Un error NACK es un mensaje XMPP normal en el que el mensaje de estado message_type es “nack”. Un mensaje NACK contiene lo siguiente:

  • Un código de error NACK
  • Una descripción de error NACK

A continuación, hay algunos ejemplos.

Registro incorrecto:

<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 no válido:

<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>

Tasa de mensajes de dispositivos excedida:

<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>

Consulta la Referencia de servidores para obtener una lista completa de los códigos de error NACK. A menos que se indique lo contrario, no se debe reintentar el envío de un mensaje con NACK. Los códigos de error NACK inesperados se deben tratar de la misma forma que INTERNAL_SERVER_ERROR.

Error de estrofa

También puedes obtener un error de estrofa en ciertos casos. Un error de estrofa contiene lo siguiente:

  • El código de error de estrofa
  • La descripción de error de estrofa (texto libre)

Por ejemplo:

<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>

Mensajes de control

Periódicamente, FCM debe cerrar una conexión para realizar el balanceo de cargas. Antes de cerrar la conexión, FCM envía un mensaje de CONNECTION_DRAINING para indicar que la conexión se está drenando y se cerrará pronto. “Drenar” se refiere a cortar el flujo de mensajes que llegan a una conexión, pero permitir que continúen los que ya están dentro de la canalización. Cuando recibes un mensaje de CONNECTION_DRAINING, debes comenzar a enviar mensajes de inmediato a otra conexión de FCM y, si es necesario, abrir una nueva. Sin embargo, debes mantener la conexión original abierta y continuar recibiendo mensajes que puedan venir a través de la conexión (y confirmarlos con ACK). FCM se encarga de iniciar el cierre de una conexión cuando está lista.

El mensaje de CONNECTION_DRAINING luce de la siguiente manera:

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

CONNECTION_DRAINING es el único control_type compatible actualmente.

Control de flujo

Cada mensaje enviado a FCM recibe una respuesta ACK o NACK. Los mensajes que no recibieron una de estas respuestas se consideran pendientes. Si el conteo de mensajes pendientes llega a 100, el servidor de apps debe dejar de enviar nuevos mensajes y esperar a que FCM reconozca algunos de los mensajes pendientes existentes, como se muestra en la figura 1:

Diagrama detallado del flujo de control entre FCM y el servidor de la app

Figura 1. Flujo de mensajes y ack.

De modo inverso, para evitar la sobrecarga del servidor de apps, FCM deja de enviar mensajes si hay demasiados mensajes sin confirmar. Por lo tanto, el servidor de apps debe confirmar con ACK los mensajes ascendentes que recibe de la aplicación cliente a través de FCM apenas pueda, para mantener un flujo constante de mensajes entrantes. El límite de mensajes pendientes mencionado previamente no corresponde a estas confirmaciones ACK. Incluso si el conteo de mensajes pendientes llega a 100, el servidor de apps debe continuar enviando confirmaciones para los mensajes recibidos de FCM a fin de evitar que se bloquee la entrega de mensajes ascendentes nuevos.

Las confirmaciones ACK son solo válidas dentro del contexto de una conexión. Si la conexión se cierra antes de que un mensaje se confirme con ACK, el servidor de apps debe esperar a que FCM reenvíe el mensaje ascendente antes de volver a intentar la confirmación. De manera similar, se deben volver a enviar todos los mensajes pendientes para los que no se recibió un ACK/NACK de FCM antes de que se cerrara la conexión.