sponsored links

Spring整合MQTT

Spring整合提供inbound与outbound通道适配器以支持MQTT协议,当前实现使用Eclipse Paho MQTT Client库。

通过DefaultMqttPahoClientFactory配置两个适配器,参考Paho文档获取关于配置选项更多的信息。

Inbound(消息驱动)通道适配器

由MqttPahoMessageDrivenChannelAdapter实现,为方便起见,可以采用命名空间的方式进行配置。最小配置可能会是这样:

<bean id="clientFactory"
  class="org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory">
 <property name="userName" value="${mqtt.username}"/>
 <property name="password" value="${mqtt.password}"/>
</bean>

<int-mqtt:message-driven-channel-adapter id="mqttInbound"
 client-id="${mqtt.default.client.id}.src"
 url="${mqtt.url}"
 topics="sometopic"
 client-factory="clientFactory"

 channel="output"/>

属性:

<int-mqtt:message-driven-channel-adapter id="oneTopicAdapter"
 client-id="foo"
 url="tcp://localhost:1883" 
 topics="bar,baz"
 qos="1,2"
 converter="myConverter"
 client-factory="clientFactory"
 send-timeout="123"
 error-channel="errors"
 recovery-interval="10000"

 channel="out" />

1)客户端id

2)代理URL

3)适配器会接受到消息的一组以逗号分隔的主题

4)以逗号分隔的一组QoS值,可以是所有主题运用单一值,或者每一个主题一个值(列表必须同样长度)

5)MqttMessageConverter(可选项),默认DefaultPahoMessageConverter生成消息带字符串载荷(默认),携带头部包括:

mqtt_topic     接收消息主题

mqtt_duplicate     如果消息重复,值为true

mqtt_qos     业务质量

DefaultPahoMessageConverter可配置为返回载荷原始byte[]类型,通过将其声明为一个实体类<bean/>,并且设定payloadAsBytes属性

6)客户端工厂

7)发送超时-如果通道可能会阻塞,才会运用(例如当前已满的边界QueueChannel)

8)错误通道--如果使用的话,ErrorMessage消息下行异常会发送至该通道,载荷为MessagingException,包含错误消息与原因

9)恢复间隔--控制在故障之后适配器会尝试重新连接的时间间隔,默认为10000ms(10s)

从4.1版本开始,编程方式改变适配器订阅的主题可以省略url,DefaultMqttPahoClientFactory属性serverURIs可以提供服务端URI,例如,这将使能连接至HA高可用簇。

从4.2.2版本开始,当适配器成功订阅至主题后,发布MqttSubscribedEvent,当连接/订阅失败时,发布MqttConnectionFailedEvent。这些事件可以由实现ApplicationListener接口的实体类获取。

新的属性recoveryInterval控制在故障之后适配器会尝试重新连接的时间间隔,默认为10000ms(10s)

在4.2.3版本之前,当适配器停止后,客户端总是会解除订阅。这是不正确的,因为如果客户端QoS大于0,我们需要保持订阅以便适配器停止时到达的消息在下一次开始时会传送。这也需要设置客户端工厂cleanSession属性为false,默认值为true。

从4.2.3版本开始,适配器不会解除订阅(默认),如果cleanSession值为false。可以重写该行为,通过设置工厂属性consumerCloseAction,可以有以下值:UNSUBSCRIBE_ALWAYS, UNSUBSCRIBE_NEVER以及UNSUBSCRIBE_CLEAN,后者(默认)会解除订阅仅当cleanSession属性值为true。

回退至4.2.3之前的行为,使用UNSUBSCRIBE_ALWAYS。

运行时增加/删除主题

从4.1版本开始,是可能的。提供方法addTopic()与removeTopic(),当增加主题时,可以可选地指明QoS(默认值为1),也可以通过发送合适的消息至<control-bus/>,携带合适的载荷修改主题:

myMqttAdapter.addTopic('foo', 1)

停止/启动适配器对主题列表无影响(不会回退至配置里原始设定值),更改不会保留至超出应用上下文生命周期;新的应用上下文会回退到配置的设定值。

当适配器停止时(或者从代理断开连接时)改变主题会在下一次连接建立时生效。

Java配置

下面的Spring Boot应用程序提供了一个运用Java配置来配置inbound适配器的例子:

@SpringBootApplication
public class MqttJavaApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(MqttJavaApplication.class)
                .web(false)
                .run(args);
    }

    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    @Bean
    public MessageProducer inbound() {
        MqttPahoMessageDrivenChannelAdapter adapter =
                new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "testClient",
                                                 "topic1", "topic2");
        adapter.setCompletionTimeout(5000);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(1);
        adapter.setOutputChannel(mqttInputChannel());
        return adapter;
    }

    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler handler() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                System.out.println(message.getPayload());
            }

        };
    }

}

Outbound通道适配器

由ConsumerEndpoint内包装的MqttPahoMessageHandler实现,为方便起见,可以采用命名空间的方式配置:

从版本4.1开始,适配器支持异步发送,避免阻塞直到传送确认,如果想要的话,应用事件可被发出使能应用确认传递。

属性:

<int-mqtt:outbound-channel-adapter id="withConverter"
    client-id="foo"
    url="tcp://localhost:1883"
    converter="myConverter"
    client-factory="clientFactory"
    default-qos="1"
    default-retained="true"
    default-topic="bar"
    async="false"
    async-events="false"
    channel="target" />

1)客户端id

2) 代理URL

3)MqttMessageConverter(可选),默认DefaultPahoMessageConverter识别以下头部:

mqtt_topic     消息发送主题

mqtt_retained     如果消息保留的话,值为true

mqtt_qos     业务质量

4)客户端工厂

5)默认业务质量(用于未发现mqtt_qos头部的情况),如果自定义converter提供的话,不允许采用

6)保留标记符默认值(用于未发现mqtt_retained头部的情况),如果自定义converter提供的话,不允许采用

7)消息发送默认主题(用于未发现mqtt_topic头部的情况)

8)当为true,当发送消息时,调用者不会阻塞等待传送确认,默认值:false(发送阻塞直到传送确认)

9)当async和async-events都为true时,发出MqttMessageSentEvent事件,包含消息、主题以及由客户端库生成的消息id,客户端id和客户端实例(每次客户端连接增加)。当传送由客户端库确认,发出MqttMessageDeliveredEvent,包含消息号、客户端号和客户端实例,使传送与发送相关联。这些事件可以由任意ApplicationListener接收,或者通过事件inbound通道适配器。注意:在MqttMessageSentEvent之前可能会接收到MqttMessageDeliveredEvent。默认值为false。

从版本4.1开始,可以省略url,DefaultMqttPahoClientFactory属性serverURIs可以提供服务器URI。例如,这将使能连接至HA高可用簇。

Java

下面的Spring Boot应用程序给出了一个使用Java配置配置outbound适配器的例子:

@SpringBootApplication
@IntegrationComponentScan
public class MqttJavaApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context =
                new SpringApplicationBuilder(MqttJavaApplication.class)
                        .web(false)
                        .run(args);
        MyGateway gateway = context.getBean(MyGateway.class);
        gateway.sendToMqtt("foo");
    }

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        factory.setServerURIs("tcp://host1:1883", "tcp://host2:1883");
        factory.setUserName("username");
        factory.setPassword("password");
        return factory;
    }

    @Bean
    @ServiceActivator(inputChannel = "mqttOutboundChannel")
    public MessageHandler mqttOutbound() {
        MqttPahoMessageHandler messageHandler =
                       new MqttPahoMessageHandler("testClient", mqttClientFactory());
        messageHandler.setAsync(true);
        messageHandler.setDefaultTopic("testTopic");
        return messageHandler;
    }

    @Bean
    public MessageChannel mqttOutboundChannel() {
        return new DirectChannel();
    }

    @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
    public interface MyGateway {
        void sendToMqtt(String data);
    }
}
Tags: MQTT