Todos os produtos
Search
Central de documentação

ApsaraMQ for MQTT:Exportar eventos de status do cliente

Última atualização: Jun 28, 2026

O ApsaraMQ for MQTT exporta eventos de conexão e desconexão de clientes para serviços downstream da Alibaba Cloud por meio de regras configuráveis. Aplicações de backend consomem esses eventos para rastrear o status de conexão, acionar fluxos de trabalho automatizados ou analisar padrões de conectividade.

Importante

As notificações de eventos de status são assíncronas e não refletem o status do cliente em tempo real. Para consultar o status atual de um cliente em tempo real, consulte Obter o status de um cliente do ApsaraMQ for MQTT.

Como funciona

Quando um cliente do ApsaraMQ for MQTT se conecta ou desconecta, o broker envia uma notificação de status para um serviço downstream da Alibaba Cloud com base na regra configurada. Aplicações de backend implantadas em instâncias do Elastic Compute Service (ECS) assinam essas notificações para detectar transições de clientes entre os estados online e offline.

Status event export flow

Como esse processo é assíncrono, uma única notificação não indica se um cliente está online no momento. Para determinar o status real, analise a linha do tempo das notificações de eventos desse cliente. Para mais detalhes, consulte Determinar o status do cliente a partir de eventos.

Casos de uso

A exportação de eventos de status dispara ações no backend quando clientes MQTT se conectam ou desconectam. Um fluxo de trabalho típico para análise de atividade de conexão inclui:

  1. O status de conexão de um cliente muda várias vezes.

  2. O broker encapsula cada alteração em uma notificação conforme a regra configurada.

  3. As notificações são entregues em um tópico do ApsaraMQ for RocketMQ.

  4. Uma aplicação de backend consome as notificações e constrói um histórico de conexões do cliente.

Limites

Item

Limite

Descrição

Regras por instância

100

Para solicitar um limite maior, entre no grupo DingTalk 116015007918 e contate o suporte técnico do ApsaraMQ for MQTT.

Deduplicação de regras

Uma regra de cada tipo por recurso interno

Por exemplo, cada grupo suporta uma regra de notificação de status do cliente, e cada tópico do ApsaraMQ for MQTT suporta uma regra de entrada de dados e uma regra de saída de dados.

Regras entre regiões

Não suportado

A source e o destino dos dados devem residir na mesma região.

Versão da instância do ApsaraMQ for MQTT

Apenas kernel V3.x.x

Verifique a versão do kernel na lista de instâncias ou na página Instance Details no console do ApsaraMQ for MQTT.

Versão da instância do ApsaraMQ for RocketMQ

Apenas ApsaraMQ for RocketMQ 4.0

Aplica-se a regras de entrada e saída de dados entre o ApsaraMQ for MQTT e o ApsaraMQ for RocketMQ.

Mapeamento de recursos

As notificações de status de todos os clientes no mesmo grupo do ApsaraMQ for MQTT são encaminhadas para o mesmo recurso downstream.

Recurso do ApsaraMQ for MQTT

Serviço da Alibaba Cloud

Recurso downstream

Definição do pacote

Grupo

ApsaraMQ for RocketMQ

Tópico

Mapeamentos de estrutura de mensagens entre ApsaraMQ for MQTT e ApsaraMQ for RocketMQ

Configurar a exportação de eventos de status

Os passos a seguir utilizam o ApsaraMQ for RocketMQ como serviço downstream.

Pré-requisitos

Antes de começar, verifique se você possui:

  • Uma instância do ApsaraMQ for MQTT com versão de kernel V3.x.x

  • Uma instância do ApsaraMQ for RocketMQ 4.0 na mesma região

  • Pelo menos um grupo configurado na instância do ApsaraMQ for MQTT

Passo 1: Crie uma regra de notificação de status do cliente

No console do ApsaraMQ for MQTT, crie uma regra e selecione os grupos cujos eventos de status do cliente você deseja exportar. Para obter os passos detalhados, consulte Criar uma regra para notificações de status do cliente.

Passo 2: Assinar notificações de status na sua aplicação de backend

Após a regra entrar em vigor, os eventos de status são entregues no tópico do ApsaraMQ for RocketMQ configurado. Assine esse tópico na sua aplicação de backend para receber os eventos. Para detalhes sobre a assinatura, consulte Assinar mensagens.

Tipos de evento

Cada notificação de status é entregue como uma mensagem do ApsaraMQ for RocketMQ. O tipo de evento é especificado na tag da mensagem.

Formato da tag: connect, disconnect ou tcpclean

Tag

Significado

Condição de disparo

connect

Cliente conectado

O cliente estabelece uma conexão com o broker.

disconnect

Cliente enviou um pacote DISCONNECT

O cliente envia um pacote DISCONNECT antes de fechar a conexão TCP, conforme o protocolo MQTT. Se o SDK do cliente não enviar esse pacote, o broker não gera um evento disconnect.

tcpclean

Conexão TCP fechada

A conexão TCP é encerrada, independentemente de o cliente ter enviado ou não um pacote DISCONNECT.

Importante

Para determinar se um cliente está offline, verifique a presença da tag tcpclean, e não da tag disconnect. Um cliente que sai inesperadamente pode não enviar um pacote DISCONNECT; portanto, o evento disconnect pode nunca aparecer.

Formato dos dados do evento

O corpo da mensagem é um objeto JSON com os seguintes campos:

Campo

Tipo

Descrição

clientId

String

O ID do cliente do ApsaraMQ for MQTT. Formato: GID_XXX@@@YYYYY.

time

Long

O timestamp em que o evento ocorreu.

eventType

String

O tipo de evento: connect, disconnect ou tcpclean.

channelId

String

O identificador exclusivo da conexão TCP.

clientIp

String

O endereço IP público e a porta do cliente MQTT.

Exemplo:

clientId: GID_XXX@@@YYYYY
time:1212121212
eventType:connect/disconnect/tcpclean
channelId:2b9b1281046046faafe5e0b458e4XXXX
clientIp:192.168.XX.XX:133XX

Determinar o status do cliente a partir de eventos

Como as notificações são assíncronas, não é possível determinar se um cliente está online com base em um único evento. Acompanhe a linha do tempo completa de eventos para cada clientId e aplique as seguintes regras:

  1. Use timestamps para ordenação. Um valor maior em time indica um evento mais recente. Compare timestamps apenas entre eventos que compartilham o mesmo clientId.

  2. **Escopo de eventos offline por channelId.** Cada channelId representa uma conexão TCP com exatamente um evento connect e um evento de fechamento (tcpclean). Um evento tcpclean invalida apenas a conexão no mesmo channelId — ele não afeta conexões em um channelId diferente.

  3. Lide com reconexões transitórias. Um cliente pode se desconectar e reconectar várias vezes, gerando múltiplos valores de channelId. Ao receber um evento tcpclean, verifique se o channelId corresponde à conexão rastreada antes de marcar o cliente como offline.

Cenário de exemplo:

Um cliente se conecta, desconecta brevemente e se reconecta:

Ordem

Evento

**channelId**

Status do cliente

1

connect

aaa111

Online (conexão aaa111 ativa)

2

tcpclean

aaa111

Offline (conexão aaa111 fechada)

3

connect

bbb222

Online (conexão bbb222 ativa)

Se você receber o evento tcpclean para aaa111 após o evento connect para bbb222, o cliente ainda está online porque bbb222 não possui um evento tcpclean correspondente.

Lógica de decisão:

For a given clientId:
  1. Maintain a map of channelId -> events.
  2. On receiving a "connect" event:
     - Store the event under its channelId.
  3. On receiving a "tcpclean" event:
     - Store the event under its channelId.
     - If the channelId has both a "connect" and a "tcpclean" event,
       that connection is closed. Remove the channelId from the map.
  4. Check if the client is online:
     - If any channelId in the map has a "connect" event but no
       "tcpclean" event, the client is online.
     - If all channelIds have been removed (or have both events),
       the client is offline.

Código de exemplo (Java)

O exemplo a seguir utiliza um consumidor do ApsaraMQ for RocketMQ para receber e processar notificações de status do cliente. O consumidor assina as tags connect e tcpclean no tópico RocketMQ designado.

Para o código-fonte completo, consulte MQTTClientStatusNoticeProcessDemo.java.

package com.aliyun.openservices.lmq.example.demo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Consumer;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class MQTTClientStatusNoticeProcessDemo {
    public static void main(String[] args) {
        // Initialize an ApsaraMQ for RocketMQ consumer to receive status notifications.
        Properties properties = new Properties();

        // The RocketMQ consumer group ID (different from the ApsaraMQ for MQTT group ID).
        properties.setProperty(PropertyKeyConst.GROUP_ID, "<your-rocketmq-group-id>");

        // AccessKey pair. Get these from the RAM console.
        properties.put(PropertyKeyConst.AccessKey, "<your-access-key>");
        properties.put(PropertyKeyConst.SecretKey, "<your-secret-key>");

        // TCP endpoint of the ApsaraMQ for RocketMQ instance.
        properties.put(PropertyKeyConst.NAMESRV_ADDR, "<your-rocketmq-endpoint>");

        // The RocketMQ topic that receives MQTT client status notifications.
        // Create this topic in the ApsaraMQ for RocketMQ console beforehand.
        final String parentTopic = "GID_XXXX_MQTT";

        // In production, use an external store (database or Redis) to persist status data
        // across application restarts.
        MqttClientStatusStore mqttClientStatusStore = new MemoryHashMapStoreImpl();

        Consumer consumer = ONSFactory.createConsumer(properties);

        // Subscribe to "connect" and "tcpclean" events only.
        consumer.subscribe(parentTopic, "connect||tcpclean",
            new MqttClientStatusNoticeListener(mqttClientStatusStore));
        consumer.start();

        // Poll the status of a specific client.
        String clientId = "GID_XXXXxXX@@@XXXXX";
        while (true) {
            System.out.println("Client online: " + checkClientOnline(clientId, mqttClientStatusStore));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Processes incoming status notifications and maintains the channel-event map.
     *
     * In a multi-server deployment, store client status in a shared external store
     * (database or Redis). Implement idempotent consumption to handle duplicate messages.
     */
    static class MqttClientStatusNoticeListener implements MessageListener {
        private MqttClientStatusStore mqttClientStatusStore;

        public MqttClientStatusNoticeListener(
            MqttClientStatusStore mqttClientStatusStore) {
            this.mqttClientStatusStore = mqttClientStatusStore;
        }

        @Override
        public Action consume(Message message, ConsumeContext context) {
            try {
                JSONObject msgBody = JSON.parseObject(new String(message.getBody()));
                System.out.println(msgBody);
                String eventType = msgBody.getString("eventType");
                String clientId = msgBody.getString("clientId");
                String channelId = msgBody.getString("channelId");
                ClientStatusEvent event = new ClientStatusEvent();
                event.setChannelId(channelId);
                event.setClientIp(msgBody.getString("clientIp"));
                event.setEventType(eventType);
                event.setTime(msgBody.getLong("time"));

                // Store the new event.
                mqttClientStatusStore.addEvent(clientId, channelId, eventType, event);

                // Read the event list for this connection.
                Set<ClientStatusEvent> events = mqttClientStatusStore.getEvent(clientId, channelId);
                if (events == null || events.isEmpty()) {
                    return Action.CommitMessage;
                }

                // If both connect and tcpclean events exist for this channelId,
                // the connection is closed. Clean up the entry.
                boolean findOnlineEvent = false;
                boolean findOfflineEvent = false;
                for (ClientStatusEvent clientStatusEvent : events) {
                    if (clientStatusEvent.isOnlineEvent()) {
                        findOnlineEvent = true;
                    } else {
                        findOfflineEvent = true;
                    }
                }
                if (findOnlineEvent && findOfflineEvent) {
                    mqttClientStatusStore.deleteEvent(clientId, channelId);
                }
                return Action.CommitMessage;
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return Action.ReconsumeLater;
        }
    }

    /**
     * Checks whether the client has an active TCP connection.
     *
     * Logic:
     * 1. If the channel map is empty, the client is offline.
     * 2. If any channelId has only a connect event (no tcpclean), the client is online.
     * 3. If all channelIds have both connect and tcpclean events, the client is offline.
     */
    public static boolean checkClientOnline(String clientId,
        MqttClientStatusStore mqttClientStatusStore) {
        Map<String, Set<ClientStatusEvent>> channelMap =
            mqttClientStatusStore.getEventsByClientId(clientId);
        if (channelMap == null) {
            return false;
        }
        for (Set<ClientStatusEvent> events : channelMap.values()) {
            boolean findOnlineEvent = false;
            boolean findOfflineEvent = false;
            for (ClientStatusEvent event : events) {
                if (event.isOnlineEvent()) {
                    findOnlineEvent = true;
                } else {
                    findOfflineEvent = true;
                }
            }
            if (findOnlineEvent & !findOfflineEvent) {
                return true;
            }
        }
        return false;
    }

}

Substitua os seguintes placeholders pelos valores reais:

Placeholder

Descrição

Exemplo

<your-rocketmq-group-id>

ID do grupo de consumidores do ApsaraMQ for RocketMQ

GID_status_consumer

<your-access-key>

AccessKey ID da Alibaba Cloud obtido no console RAM

LTAI5tXxx

<your-secret-key>

AccessKey secret da Alibaba Cloud obtido no console RAM

xXxXxXx

<your-rocketmq-endpoint>

Endpoint TCP da instância do ApsaraMQ for RocketMQ

http://MQ_INST_xxx.mq-internet-access.mq-internet.aliyuncs.com:80

Referências