前提:先安装Mosquitto并启动服务,可使用mqttx进行接收发送的测试。
Mosquitto以配置启动命令
mosquitto -c mosquitto.conf -v
原文链接:mqtt的使用
本文为测试使用固无账号密码,可在原文查看
封装后实现效果,加入一个新的topic时新建一个类进行自动订阅,该类写所订阅topic方法的处理。发送消息看原文使用方法。
所用依赖
<!--mqtt包--><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-mqtt</artifactId><version>5.5.14</version></dependency><!--gson包--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.9.0</version></dependency>
配置文件
spring:mqtt:url: tcp://127.0.0.1:1883clientId: mqttx_867916f3completionTimeout: 2000
封装后代码,链接mqtt工具类
package Ceshi.configure.mqtt;import javax.annotation.PostConstruct;import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Configuration
public class MqttConsumerConfig {@Value("${spring.mqtt.url}")private String hostUrl;@Value("${spring.mqtt.clientId}")private String clientId;/*** 客户端对象*/private MqttClient client;/*** 在bean初始化后连接到服务器*/@PostConstructpublic void init(){connect();}/*** 客户端连接服务端*/public void connect(){try {//创建MQTT客户端对象client = new MqttClient(hostUrl,clientId,new MemoryPersistence());//连接设置MqttConnectOptions options = new MqttConnectOptions();//是否清空session,设置为false表示服务器会保留客户端的连接记录,客户端重连之后能获取到服务器在客户端断开连接期间推送的消息//设置为true表示每次连接到服务端都是以新的身份options.setCleanSession(true);//设置超时时间,单位为秒options.setConnectionTimeout(100);//设置心跳时间 单位为秒,表示服务器每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线options.setKeepAliveInterval(20);//设置遗嘱消息的话题,若客户端和服务器之间的连接意外断开,服务器将发布客户端的遗嘱信息options.setWill("willTopic",(clientId + "与服务器断开连接").getBytes(),0,false);//设置回调client.setCallback(new MqttConsumerCallBack());client.connect(options);} catch (MqttException e) {e.printStackTrace();}}/*** 断开连接*/public void disConnect(){try {client.disconnect();} catch (MqttException e) {e.printStackTrace();}}/*** 订阅主题*/public void subscribe(String topic,int qos){try {client.subscribe(topic,qos);} catch (MqttException e) {e.printStackTrace();}}/*** @Description: 消息发布*/public void publish(int qos,boolean retained,String topic,String message){MqttMessage mqttMessage = new MqttMessage();mqttMessage.setQos(qos);mqttMessage.setRetained(retained);mqttMessage.setPayload(message.getBytes());//主题的目的地,用于发布/订阅信息MqttTopic mqttTopic = client.getTopic(topic);//提供一种机制来跟踪消息的传递进度//用于在以非阻塞方式(在后台运行)执行发布是跟踪消息的传递进度MqttDeliveryToken token;try {//将指定消息发布到主题,但不等待消息传递完成,返回的token可用于跟踪消息的传递状态//一旦此方法干净地返回,消息就已被客户端接受发布,当连接可用,将在后台完成消息传递。token = mqttTopic.publish(mqttMessage);token.waitForCompletion();} catch (MqttException e) {e.printStackTrace();}}
}
监听消息工具类
package Ceshi.configure.mqtt;import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Value;import java.util.HashMap;
import java.util.Map;public class MqttConsumerCallBack implements MqttCallback{@Value("${spring.mqtt.clientId}")private String clientId;private static Map<String,JIantingChuli> maps=new HashMap<>();public static void setJIantingChulis(String k,JIantingChuli y){maps.put(k,y);}/*** 客户端断开连接的回调*/@Overridepublic void connectionLost(Throwable throwable) {System.out.println(clientId+"与服务器断开连接,可重连");}/*** 消息到达的回调*/@Overridepublic void messageArrived(String topic, MqttMessage message) throws Exception {System.out.println(String.format("接收消息主题 : %s",topic));System.out.println(String.format("接收消息Qos : %d",message.getQos()));System.out.println(String.format("接收消息内容 : %s",new String(message.getPayload())));System.out.println(String.format("接收消息retained : %b",message.isRetained()));maps.get(topic).xiaoxifenhua(new String(message.getPayload()));//调用方法进行消费}/*** 消息发布成功的回调*/@Overridepublic void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {IMqttAsyncClient client = iMqttDeliveryToken.getClient();System.out.println(client.getClientId()+"发布消息成功!");}}
统一处理消息接口
public interface JIantingChuli {//根据fname找到要执行的方法,后者为传参void xiaoxifenhua(String message);
}
接收报文格式类
import lombok.AllArgsConstructor;
import lombok.Data;import java.io.Serializable;@Data
@AllArgsConstructor
public class MqResult implements Serializable{private String key;//方法名private Object data;//方法所需参数
}
一个测试订阅类(重点)(自己根据需求几个topic复制几个,内部topic与qos看情况修改,xiaoxifenhua内有几个方法就写几个分支,k为方法名)
import Ceshi.configure.mqtt.JIantingChuli;
import Ceshi.configure.mqtt.MqResult;
import Ceshi.configure.mqtt.MqttConsumerCallBack;
import Ceshi.configure.mqtt.MqttConsumerConfig;
import Ceshi.param.AppCodeApiParam;
import com.google.gson.Gson;
import lombok.Data;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Component
public class Ceshi1 implements JIantingChuli {@Resourceprivate MqttConsumerConfig mqttConsumerConfig;private Gson gson=new Gson();private String topic="ceshi";private int qos=2;@PostConstructpublic void chushihua(){mqttConsumerConfig.subscribe(topic,qos);//添加到订阅MqttConsumerCallBack.setJIantingChulis(topic,this);//监听到消息时找到此类对象System.out.println(topic+","+qos+"已注册订阅");}@Overridepublic void xiaoxifenhua(String message) {MqResult mqres=gson.fromJson(message,MqResult.class);//json解析为对象switch (mqres.getKey()){case "ceshi":ceshi(gson.fromJson(mqres.getData().toString(), AppCodeApiParam.class));//AppCodeApiParam是随便一个类,内部有个getPassword方法显示参数值break;}}//随便写的测试方法tpublic void ceshi(AppCodeApiParam appCodeApiParam){System.out.println(appCodeApiParam.getPassword());}
}
QoS0,At most once,至多一次;
QoS1,At least once,至少一次;
QoS2,Exactly once,确保只有一次。
因上方测试类topic=“ceshi”,所以我们用MqttX进行测试一下(试了一下发送时qos等于啥都能接收到,只要topic相等)
参数(按照参数格式来key是要调用的方法名,data是所传参数)
{"key": "ceshi",//方法名"data": {//要传的参数"password":"132465"}
}
启动后可看见已注册订阅
发送消息测试结果,可见触发的方法与传递的参数均无误