1.pom
<!-- mqtt-->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
<!-- json转对象-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.13</version>
</dependency>
2.配置
几乎所有的东西都在配置类中
/***
* 配置类 只有mqtt.services配置时才会加载此类
*/
@Configuration
@ConditionalOnProperty("mqtt.services")
public class MQTTConfig implements ApplicationListener<ApplicationEvent> {
/**
* 发送数据使用的接口
*/
@Autowired
MsgGateway msgGateway;
//private final MsgSendService msgSendService;//发布消息到消息中间件接口
/**
* 接收数据客户端ID
*/
@Value("${mqtt.appid:mqtt_id}")
private String appid;
/**
* 发送数据时客户端ID 不能与接收数据的appid相同
* 在集成的时候入站与出站消息处理并不使用同一个连接,所以如果clien ID相同,将会出现Mqtt反复重连现象,实为 mqtt 出入站连接交替踢对方下线
*/
@Value("${mqtt.outAppId:mqtt_out_id}")
private String outAppId;
/**
* 订阅主题,可以是多个主题
*/
@Value("${mqtt.input.topic:mqtt_input_topic}")
private String[] inputTopic;
/**
* 发布主题,可以是多个主题
*/
@Value("${mqtt.out.topic:mqtt_out_topic}")
private String[] outTopic;
/**
* 服务器地址以及端口
*/
@Value("${mqtt.services:#{null}}")
private String[] mqttServices;
/**
* emqx服务器用户名
*/
@Value("${mqtt.user:#{null}}")
private String user;
/**
* emqx服务器密码
*/
@Value("${mqtt.password:#{null}}")
private String password;
/**
* 心跳时间,默认为5分钟
*/
@Value("${mqtt.KeepAliveInterval:300}")
private Integer KeepAliveInterval;
/**
* 是否不保持session,默认为session保持
*/
@Value("${mqtt.CleanSession:false}")
private Boolean CleanSession;
/**
* //是否自动重联,默认为开启自动重联
*/
@Value("${mqtt.AutomaticReconnect:true}")
private Boolean AutomaticReconnect;
/**
* 连接超时,默认为30秒
*/
@Value("${mqtt.CompletionTimeout:30000}")
private Long CompletionTimeout;
/**
* 通信质量,详见MQTT协议
*/
@Value("${mqtt.Qos:1}")
private Integer Qos;
// public MQTTConfig(MsgSendService msgSendService) {
// this.msgSendService = msgSendService;
// }
/**
* MQTT连接配置
* @return 连接工厂
*/
@Bean
public MqttPahoClientFactory mqttClientFactory() {
//连接工厂类
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
//连接参数
MqttConnectOptions options = new MqttConnectOptions();
//连接地址
options.setServerURIs(mqttServices);
if(null!=user) {
//用户名
options.setUserName(user);
}
if(null!=password) {
//密码
options.setPassword(password.toCharArray());
}
//心跳时间
options.setKeepAliveInterval(KeepAliveInterval);
//断开是否自动重联
options.setAutomaticReconnect(AutomaticReconnect);
//保持session
options.setCleanSession(CleanSession);
factory.setConnectionOptions(options);
return factory;
}
/**
* 注入这个adapter 在mqttservice中使用 动态增加和删除订阅的topic
* @param factory
* @return
*/
@Bean
public MqttPahoMessageDrivenChannelAdapter adapter(MqttPahoClientFactory factory){
return new MqttPahoMessageDrivenChannelAdapter(appid,factory,inputTopic);
}
/**
* 入站管道 管理消息接收的 采用配置文件中配置的topic
* @param mqttPahoClientFactory
* @return MessageProducerSupport
*/
@Bean
public MessageProducerSupport mqttInput(MqttPahoClientFactory mqttPahoClientFactory){
//建立订阅连接
// MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(appid, mqttPahoClientFactory, inputTopic);
MqttPahoMessageDrivenChannelAdapter adapter = adapter(mqttPahoClientFactory);
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
//bytes类型接收
converter.setPayloadAsBytes(true);
//连接超时的时间
adapter.setCompletionTimeout(CompletionTimeout);
adapter.setConverter(converter);
//消息质量
adapter.setQos(Qos);
//输入管道名称 自定义
adapter.setOutputChannelName(ChannelName.INPUT_DATA);
return adapter;
}
/**
* 向服务器发送数据管道绑定 管理发送的 指定发送时的客户端id
* @param connectionFactory tcp连接工厂类
* @return 消息管道对象
*/
@Bean
@ServiceActivator(inputChannel = ChannelName.OUTPUT_DATA_MQTT)
public AbstractMqttMessageHandler MQTTOutAdapter(MqttPahoClientFactory connectionFactory) {
//创建一个新的出站管道,由于MQTT的发布与订阅是两个独立的连接,因此客户端的ID(即APPID)不能与订阅时所使用的ID一样,否则在服务端会认为是同一个客户端,而造成连接失败
MqttPahoMessageHandler outGate = new MqttPahoMessageHandler(outAppId, connectionFactory);
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
//bytes类型接收
converter.setPayloadAsBytes(true);
outGate.setAsync(true);
//设置连接超时时时
outGate.setCompletionTimeout(CompletionTimeout);
//设置通信质量
outGate.setDefaultQos(Qos);
outGate.setConverter(converter);
return outGate;
}
/**
* MQTT连接时调用的方法
* @param event
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof MqttSubscribedEvent) {
String msg = "OK";
/**------------------连接时需要发送起始消息,写在这里-------------**/
//MsgGateway.send(MessageBuilder.withPayload(msg.getBytes()).build());
msgGateway.sendWithTopic(outTopic[0],"hello");
}
}
/**
* 消息接收回调 收到订阅的消息会到此处理
* @return
*/
@Bean
//使用ServiceActivator 指定接收消息的管道为 mqttInboundChannel,投递到mqttInboundChannel管道中的消息会被该方法接收并执行
@ServiceActivator(inputChannel = ChannelName.INPUT_DATA)
public MessageHandler handleMessage() {
return message -> {
//将字节数组转为字符串
String msg = new String((byte[]) message.getPayload());
//将json字符串转为对象
Device device = JSON.parseObject(msg, Device.class);
// System.out.printf(jsonObject.get("msg").toString());
System.out.println(device);
//回复消息到指定topic
msgGateway.sendWithTopic(outTopic[0],"ok");
};
}
}
3.配置类中用到的辅助类
1.通道类型
public class ChannelName {
/**
* 入站管道
*/
public final static String INPUT_DATA="input_data";
/**
* TCP出站管道
*/
public final static String OUTPUT_DATA_TCP="output_data_TCP";
/**
* mqtt出站管道名称
*/
public final static String OUTPUT_DATA_MQTT="output_data_MQTT";
}
2.发送数据接口
定义发送数据的方法
@MessagingGateway
@Component
public interface MsgGateway {
/**
* MQTT 发送网关
* @param a 主题,可以指定不同的数据发布主题,在消息中间件里面体现为不同的消息队列
* @param out 消息内容
*/
@Gateway(requestChannel = ChannelName.OUTPUT_DATA_MQTT)
void send(@Header(MqttHeaders.TOPIC) String a, Message<byte[]> out);
@Gateway(requestChannel = ChannelName.OUTPUT_DATA_MQTT)
void sendToMqtt(String text);
//指定topic发送
@Gateway(requestChannel = ChannelName.OUTPUT_DATA_MQTT)
void sendWithTopic(@Header(MqttHeaders.TOPIC) String topic, String text);
void sendWithTopicAndQos(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) Integer Qos, String text);
}
3.模拟实体类
@Data
public class Device {
String id;
Double tem;
Double press;
Double hum;
}
4.配置文件
# 应用名称
spring.application.name=testmqttnew
# 应用服务 WEB 访问端口
server.port=8045
#--------MQTT---------------------------
#设备ID,唯一标识
mqtt.appid=mqtt_id
#发送设备id
mqtt.outAppId=mqtt_out_id
#订阅主题,多个主题用逗号分隔
mqtt.input.topic=/testservice
#发布主题
mqtt.out.topic=/testserver
#MQTT服务器地址,可以是多个地址
mqtt.services=tcp://192.168.56.10:1883
#mqtt用户名,默认无
mqtt.user=zhangsan
#mqtt密码,默认无
mqtt.password=zhangsan
#心跳间隔时间,默认3000
#mqtt.KeepAliveInterval=3000
#是否不保持session,默认false
#mqtt.CleanSession=false
#是否自动连接,默认true
#mqtt.AutomaticReconnect=true
#连接超时,默认30000
#mqtt.CompletionTimeout=30000
#传输质量,默认1
#mqtt.Qos=1
5.实际测试
在mqttx订阅发送主题 可以收到代码端的回复
6.动态更新topic
controller
@RestController
public class MqttController {
@Autowired
MqttService mqttService;
@GetMapping("/addTopic")
public String addTopic(String topic){
mqttService.addTopic(topic);
return "addok!";
}
@GetMapping("/deleteTopic")
public String deleteTopic(String topic){
mqttService.removeTopic(topic);
return "removeok!";
}
}
service
@Service
public class MqttService {
MqttPahoMessageDrivenChannelAdapter adapter;
//
@Autowired
public MqttService(MqttPahoMessageDrivenChannelAdapter adapter) {
this.adapter = adapter;
}
public void addTopic(String topic) {
String[] topics = adapter.getTopic();
if(!Arrays.asList(topics).contains(topic)){
adapter.addTopic(topic,0);
}
}
public void removeTopic(String topic) {
adapter.removeTopic(topic);
}
}
发送请求测试
1.新增
发送数据至新主题,有回复