2. SpringBoot + MQTT 门禁设备对接实战
项目demo文件
源码地址: SynerTools 项目地址 (qq.com)
业务流程
首先,我们的 Spring Boot 服务充当一个客户端,而人脸识别设备的内置系统也作为另一个客户端。两个客户端通过 EMQX 服务端进行通信,负责指令的发送与接收
为什么使用MQTT而不是HTTP协议?
- 低带宽消耗:MQTT协议非常轻量级,设计之初就考虑到了低带宽网络环境,因此在传输数据时消耗的带宽非常少。相比之下,HTTP协议的数据包头较大,带宽消耗更高。
- 能效优化:MQTT协议设计简洁,特别适用于功耗敏感的物联网设备。HTTP协议因为连接开销大和数据包头大,通常会消耗更多的电能。
- 异步解耦: mqtt和rabbitmq是有相似之处, 都支持发布/订阅模式和异步通信,使消息发送者和接收者可以解耦,适合分布式系统应用,并且它们都提供消息可靠性保证和多语言支持
技术栈
- mica-mqtt(客户端) : mica-mqtt地址 (maven依赖引入即可)
Mica-MQTT 简化了 Spring Boot 项目中 MQTT 的集成,通过自动化配置和高度可扩展的设计,提供了便捷的消息发布和订阅功能。它支持多种 MQTT 客户端,具有高稳定性和可靠性,适用于生产环境。
- emqx开源版 (服务端) : 服务端下载地址
EMQX 开源版提供了高性能和高并发的 MQTT 消息处理能力,支持大规模设备连接。其用户友好的管理界面使页面管控方便,支持实时监控和配置管理。联调方面,丰富的调试工具和插件扩展功能简化了开发和测试过程。
设备对接业务流程
设备名称: 人脸识别机
MQTT协议对接文档 : 【免费】MQTT协议设备对接文档资源-CSDN文库
设备消息的推送与接收
根据人脸录入流程图可以看出,我们的系统实际上是一个基于 Spring Boot 的考勤客户端。为了实现消息推送功能,我们需要借助 mica-mqtt 客户端来进行消息推送。
1. 推送消息
这里我们以向设备推送人脸消息为例
这里的 deviceNo(设备编号 ,一般在贴在设备的背面)就是指设备的 SN 号。我们需要将其替换为对应的编号,才能向指定设备推送消息。确认好发送的主题后,剩下的 JSON 内容我们只需按照文档里的要求来编写即可。
/**
* @author wsq
*/
@Service
public class MainService {
private static final Logger logger = LoggerFactory.getLogger(MainService.class);
@Autowired
private MqttClientTemplate client;
public boolean publish() {
client.publish("pass/002024041590421/request", "对应的人脸信息JSON数据".getBytes(StandardCharsets.UTF_8));
return true;
}
}
推送消息演示 :
2. 接收消息
以为监听同行记录上报为例
这里的 deviceNo(设备编号) 就是指的设备sn号, 如果你想监听多台设备的话就可以 用 "+" (单层通配符)代替
如果想了解更多通配符可以参考这篇文章 : 通过案例理解 MQTT 主题与通配符 | EMQ (emqx.com)
所以对应我们的主题名称为 @MqttClientSubscribe("/face/+/request")
@Service
public class MqttClientSubscribeListener {
private static final Logger logger = LoggerFactory.getLogger(MqttClientSubscribeListener.class);
@MqttClientSubscribe("/face/+/request")
public void subQos0(String topic, byte[] payload) {
logger.info("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
}
}
接收消息演示:
客户端页面
在设备和客户端启动后,以及Spring Boot客户端成功运行时,您可以在EMQX的管理界面上看到它们的状态显示。访问地址 : http://127.0.0.1:18083
设备对接的坑点和注意事项
- emqx 的消息体内容大小太大, 导致服务端和客户端断开连接 解决办法 : 调整报文大小5M基本合适
- 苹果手机上传后照片是歪着的, 导致设备无法提取人脸特征值 解决办法: 判断宽高width > height 则使照片顺时针旋转90度, 代码如下
/**
* 根据图片URL,先进行压缩处理,再将其转换为Base64编码字符串。
*
* @return Base64编码的字符串,若输入URL为空或无效,则返回null
*/
public static String compressAndEncodeToBase64(BufferedImage image) throws Exception {
if (image == null) {
return null;
}
System.out.println(image.getHeight() + ":" + image.getWidth());
// 检查图像是否需要旋转
if (image.getWidth() > image.getHeight()) {
image = rotateImage90DegreesClockwise(image);
}
BufferedImage compressedImage = Thumbnails.of(image)
.imageType(BufferedImage.TYPE_INT_RGB)
.size(image.getWidth() / 4, image.getHeight() / 4)
.keepAspectRatio(true)
.asBufferedImage();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(compressedImage, "jpg", baos);
byte[] bytes = baos.toByteArray();
return java.util.Base64.getEncoder().encodeToString(bytes);
}
/**
* 照片旋转90操作 为解决苹果手机上传后转base64图片歪着的问题
*/
private static BufferedImage rotateImage90DegreesClockwise(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage rotatedImage = new BufferedImage(height, width, image.getType());
Graphics2D g2d = rotatedImage.createGraphics();
g2d.translate((height - width) / 2.0, (height - width) / 2.0);
g2d.rotate(Math.toRadians(90), height / 2.0, width / 2.0);
g2d.drawRenderedImage(image, null);
g2d.dispose();
return rotatedImage;
}
- 设备自己也需要连接一个mqtt服务端(本文使用的EMQX), 一般是内网地址登录设备的ip就能设置(一定要让设备先联网!)
- 厂家有提供他们的说明文档,先把文档大致看下,然后再和厂家沟通;