建议先阅读文末参考文章再回看本文实现
mqtt是发布订阅机制,消息发送方无法立即知道消息订阅方的返回消息。在一些场景下比如我通过mqtt给某温度传感器下发命令读取该设备的温度数据这时需要同步得到温度传感器返回的数据,这就需要把异步请求转化为同步请求。
下面参考https://www.cnblogs.com/goodAndyxublog/p/12393723.html所描述mq方案来实现。
1.方案整体流程
1.发送mqtt消息生成DefaultFuture,等待返回消息。DefaultFuture的实现原理即为dubbo中DefaultFuture实现。DefaultFuture是本方案的核心
@RestController
@RequestMapping("mqtt")
public class TestController {
@Autowired
SynMqttSender synMqttSender;
@PostMapping("send")
public Message sendMsg(@RequestBody Message message){
DefaultFuture future = synMqttSender.sendMessage(message);
return future.get();
}
}
2 mqtt返回消息处理程序
/**
* MQTT消息处理器(消费者)
*/
@Bean
@ServiceActivator(inputChannel = CHANNEL_NAME_IN)
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
if(topic.equals(Constant.MQTT_TOPIC_RES)){
String msg = message.getPayload().toString();
mqttResHandler.deal(JSONUtil.toBean(msg,com.ffy.mqtt.model.Message.class));
}
String msg = message.getPayload().toString();
log.info("\n--------------------START-------------------\n" +
"接收到订阅消息:\ntopic:" + topic + "\nmessage:" + msg +
"\n---------------------END--------------------");
}
};
}
@Component
public class MqttResHandler {
public void deal(Message msg){
//根据messageId判断是否是本机发送的消息
Long msgId = msg.getMessageId();
if(DefaultFuture.contains(msgId)){
DefaultFuture.received(msg);
}
}
}
在参考文章中当接收到的异步返回的消息后通过mq广播给了所有机器进行处理,本场景中各服务订阅topic本身就能保证返回的消息被所有机器接收到。
实现要点在于DefeaultFuture的实现,参考文章已有详细说明。
2.最终效果演示
1.发送消息
2.为message生成一个唯一的messageId通过mqtt发送给订阅方。用mqtt.x模拟温度传感器订阅消息
3.返回响应消息带上接收到的messageId
4 根据messageId找到唤醒等待的DefaultFuture返回数据
本文采用rabbitmq作为mqtt-broker
完整代码:https://github.com/setadd/mqtt
参考资料: