设备连接物联网平台后,数据直接上报至物联网平台,平台上的数据可以通过AMQP通道流转至您的服务器。本文中我们将配置AMQP服务端订阅功能。您的服务器可以通过接入AMQP客户端,接收路灯设备的数据。

前提条件

已接入设备和上报数据。具体操作,请参见设备接入和上报数据

配置AMQP服务端订阅

  1. 登录物联网平台控制台
  2. 实例概览页,找到对应的企业版实例,单击实例进入实例详情页。
  3. 在左侧导航栏,选择规则引擎 > 服务端订阅
  4. 服务端订阅页,单击创建订阅
  5. 创建订阅对话框中,完成配置,单击确认
    参数 说明
    产品 选择家庭温控器
    订阅类型 选择为AMQP
    消费组 单击选择目标消费组对话框右下角的创建消费组,新建消费组(例如:温控器数据接收)。消费组相关说明,请参见管理消费组
    推送消息类型 服务端要订阅的消息类型。本示例选择设备上报消息物模型历史数据上报。目前,服务端可订阅的设备消息类型包括:
    • 设备上报消息:产品下所有设备Topic列表中,操作权限发布的Topic中的消息。更多信息,请参见什么是Topic

      设备上报消息,包括设备上报的自定义数据和物模型数据(属性上报、事件上报、属性设置响应和服务调用响应)。推送到服务端的物模型数据是经物联网平台系统处理过后的数据,数据格式请参见数据格式

      例如,一个产品有3个Topic类,分别是:
      • /${YourProductKey}/${YourDeviceName}/user/get,具有订阅权限。
      • /${YourProductKey}/${YourDeviceName}/user/update,具有发布权限。
      • /${YourProductKey}/${YourDeviceName}/thing/event/property/post,具有发布权限。

      那么,服务端订阅会推送具有发布权限的Topic类中的消息,即/${YourProductKey}/${YourDeviceName}/user/update/${YourProductKey}/${YourDeviceName}/thing/event/property/post中的消息。

      说明 如果需要监听自定义Topic消息,请将自定义Topic的数据转发到AMQP服务端订阅消费组,再通过AMQP客户端,监听相关消息。更多信息,请参见云产品流转概述
    • 设备状态变化通知:该产品下的设备上下线状态变化时通知的消息。
    • 网关子设备发现上报:网关将发现的子设备信息上报给物联网平台。需要网关上的应用程序支持。网关产品特有消息类型。
    • 设备拓扑关系变更:子设备和网关之间的拓扑关系建立和解除消息。网关产品特有消息类型。
    • 设备生命周期变更:设备创建、删除、禁用、启用等消息。
    • 物模型历史数据上报:设备上报的属性和事件历史数据。
    • OTA升级状态通知:验证升级包和批量升级时,设备升级成功或失败的事件通知。
    • 设备标签变更:设备上报的标签变更消息。
    • OTA模块版本号上报:设备上报的OTA模块版本号变更消息。
    • OTA升级批次状态通知:设备OTA升级批次状态变化通知。
您也可以通过云产品流转,将数据转发到创建的消费组(例如:温控器数据接收),使AMQP客户端接收消息。更多信息,请参见数据转发到AMQP服务端订阅消费组添加操作

Java SDK接入示例

  1. 打开IntelliJ IDEA,创建一个Maven工程。例如Amqp
  2. 在工程中的pom.xml文件中,添加Maven依赖,然后单击Load Maven Changes图标,完成依赖包下载。
    <!-- amqp 1.0 qpid client -->
            <dependency>
                <groupId>org.apache.qpid</groupId>
                <artifactId>qpid-jms-client</artifactId>
                <version>0.47.0</version>
            </dependency>
            <!-- util for base64-->
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>1.10</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.25</version>
                <scope>compile</scope>
            </dependency>
  3. 在路径Amqp\src\main\java下,创建Java类(例如AmqpJavaClientDemo),将AmqpJavaClientDemo.java文件内容替换为以下代码。
    import java.net.URI;
    import java.util.Hashtable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.Destination;
    import javax.jms.Message;
    import javax.jms.MessageConsumer;
    import javax.jms.MessageListener;
    import javax.jms.MessageProducer;
    import javax.jms.Session;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import org.apache.commons.codec.binary.Base64;
    import org.apache.qpid.jms.JmsConnection;
    import org.apache.qpid.jms.JmsConnectionListener;
    import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    //此处类名AmqpJavaClientDemo与创建的Java类文件名必须一致。
    public class AmqpJavaClientDemo {
    
        private final static Logger logger = LoggerFactory.getLogger(AmqpJavaClientDemo.class);
    
        //业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
        private final static ExecutorService executorService = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(50000));
    
        public static void main(String[] args) throws Exception {
            //参数说明,请参见AMQP客户端接入说明文档。
            String accessKey = " LTAI4GFGQvKuqHJhFa******";
            String accessSecret = "iMS8ZhCDdfJbCMeA005sieKe******";
            String consumerGroupId = "yAaYEd9qtWeJt66qKUU0******";
            //iotInstanceId:企业版实例请填写实例ID,公共实例请填空字符串""。
            String iotInstanceId = "iot-cn-6ja1y******"; 
            long timeStamp = System.currentTimeMillis();
            //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
            String signMethod = "hmacsha1";
            //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
            //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
            String clientId = "12345";
    
            //userName组装方法,请参见AMQP客户端接入说明文档。
            String userName = clientId + "|authMode=aksign"
                + ",signMethod=" + signMethod
                + ",timestamp=" + timeStamp
                + ",authId=" + accessKey
                + ",iotInstanceId=" + iotInstanceId
                + ",consumerGroupId=" + consumerGroupId
                + "|";
            //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
            String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
            String password = doSign(signContent,accessSecret, signMethod);
            //接入域名,请参见AMQP客户端接入说明文档。
            String connectionUrl = "failover:(amqps://iot-cn-6ja1y******.amqp.iothub.aliyuncs.com:5671?amqp.idleTimeout=80000)"
                + "?failover.reconnectDelay=30";
    
            Hashtable<String, String> hashtable = new Hashtable<>();
            hashtable.put("connectionfactory.SBCF",connectionUrl);
            hashtable.put("queue.QUEUE", "default");
            hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
            Context context = new InitialContext(hashtable);
            ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
            Destination queue = (Destination)context.lookup("QUEUE");
            // 创建连接。
            Connection connection = cf.createConnection(userName, password);
            ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
            // 创建会话。
            // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
            // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            connection.start();
            // 创建Receiver连接。
            MessageConsumer consumer = session.createConsumer(queue);
            consumer.setMessageListener(messageListener);
        }
    
        private static MessageListener messageListener = new MessageListener() {
            @Override
            public void onMessage(Message message) {
                try {
                    //1.收到消息之后一定要ACK。
                    // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
                    // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
                    // message.acknowledge();
                    //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
                    // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
                    executorService.submit(() -> processMessage(message));
                } catch (Exception e) {
                    logger.error("submit task occurs exception ", e);
                }
            }
        };
    
        /**
         * 在这里处理您收到消息后的具体业务逻辑。
         */
        private static void processMessage(Message message) {
            try {
                byte[] body = message.getBody(byte[].class);
                String content = new String(body);
                String topic = message.getStringProperty("topic");
                String messageId = message.getStringProperty("messageId");
                logger.info("receive message"
                    + ", topic = " + topic
                    + ", messageId = " + messageId
                    + ", content = " + content);
            } catch (Exception e) {
                logger.error("processMessage occurs error ", e);
            }
        }
    
        private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
            /**
             * 连接成功建立。
             */
            @Override
            public void onConnectionEstablished(URI remoteURI) {
                logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
            }
    
            /**
             * 尝试过最大重试次数之后,最终连接失败。
             */
            @Override
            public void onConnectionFailure(Throwable error) {
                logger.error("onConnectionFailure, {}", error.getMessage());
            }
    
            /**
             * 连接中断。
             */
            @Override
            public void onConnectionInterrupted(URI remoteURI) {
                logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
            }
    
            /**
             * 连接中断后又自动重连上。
             */
            @Override
            public void onConnectionRestored(URI remoteURI) {
                logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
            }
    
            @Override
            public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
    
            @Override
            public void onSessionClosed(Session session, Throwable cause) {}
    
            @Override
            public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
    
            @Override
            public void onProducerClosed(MessageProducer producer, Throwable cause) {}
        };
    
        /**
         * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
         */
        private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
            SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
            Mac mac = Mac.getInstance(signMethod);
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(toSignString.getBytes());
            return Base64.encodeBase64String(rawHmac);
        }
    }
    参数说明
    参数 示例 说明
    accessKey LTAI4GFGQvKuqHJhFa******

    登录物联网平台控制台,将鼠标移至账号头像上,然后单击AccessKey管理,获取AccessKey ID和AccessKey Secret。

    说明 如果使用RAM用户,您需授予该用户管理物联网平台的权限(AliyunIOTFullAccess),否则将连接失败。授权方法请参见授权RAM用户访问物联网平台
    accessSecret iMS8ZhCDdfJbCMeA005sieKe******
    consumerGroupId yAaYEd9qtWeJt66qKUU0****** 消费组ID。

    登录物联网平台控制台,在对应实例的规则引擎 > 服务端订阅 > 消费组列表查看您的消费组ID。

    iotInstanceId iot-cn-6ja1y****** 企业版实例的接入域名,请在物联网平台控制台,找到对应的实例,单击实例,进入实例详情页面查看。
    clientId 12345 表示客户端ID,建议使用您的AMQP客户端所在服务器UUID、MAC地址、IP等唯一标识。长度不可超过64个字符。

    登录物联网平台控制台,在对应实例的规则引擎 > 服务端订阅 > 消费组列表,单击消费组对应的查看消费组详情页将显示该参数,方便您识别区分不同的客户端。

    Url iot-cn-6ja1y******.amqp.iothub.aliyuncs.com 企业版实例的接入域名,请在物联网平台控制台,找到对应的实例,单击实例,进入实例详情页面查看。

    更多信息,请参见AMQP客户端接入说明

  4. 运行AmqpJavaClientDemo.java文件,执行结果如下图。
    SDK运行结果
    返回参数:
    参数 参数值 说明
    topic /***********/******/thing/event/property/post 设备属性上报的Topic
    messageID 1324198300680719360 消息的ID
    content {"temperature":23,"humidity":21,"time":1604548451951} 消息的内容

代码运行成功后,您可以在服务端订阅中,查看消费组状态的基本信息。

具体操作,请参见管理消费组

消费组状态

查看日志

所有配置完成后,登录物联网平台控制台,在企业实例下的监控运维 > 日志服务中,从产品列表选择家庭温控器,可在云端运行日志页签下,查看完整的日志信息。

日志信息

后续步骤

云端下发指令