一、业务场景
好多朋友在咨询如何得知(监测)MQTT客户端状态(在线、离线),或者可以说是监测采集器状态。
其实这个在MQTT协议中已经给出系统主题,MQTT服务端可以知道客户端的任何情况,比如:什么时候上线和下线。
二、解决方案
$SYS/brokers/${node}/clients/${clientid}/connected :上线事件。当某客户端上线时,会向该主题(Topic)发布消息
$SYS/brokers/${node}/clients/${clientid}/disconnected :掉线事件。当某客户端掉线时,会向该主题(Topic)发布消息
当然在开发的时候我们订阅两个主题比较麻烦,在这里我们可以采用主题通配符模式直接订阅一个主题即可:
$SYS/brokers/+/clients/#
private void initMonitor() { String subTopic = "$SYS/brokers/+/clients/#"; String clientId = "monitor_mqtt_emqx"+new Date().getTime(); MemoryPersistence persistence = new MemoryPersistence();
try { MqttClient client = new MqttClient(broker, clientId, persistence);
// MQTT 连接选项 MqttConnectOptions connOpts = new MqttConnectOptions(); connOpts.setUserName("admin"); connOpts.setPassword("123456".toCharArray()); // 保留会话 //connOpts.setCleanSession(true); //设置重连 connOpts.setAutomaticReconnect(true);
// 设置回调 client.setCallback(new OnMonitorCallback());
// 建立连接 System.out.println("monitor_Connecting to broker: " + broker); client.connect(connOpts);
System.out.println("monitor_Connected"); //System.out.println("Publishing message: " + content);
// 订阅 client.subscribe(subTopic); } catch (MqttException me) { System.out.println("monitor_reason " + me.getReasonCode()); System.out.println("monitor_msg " + me.getMessage()); System.out.println("monitor_loc " + me.getLocalizedMessage()); System.out.println("monitor_cause " + me.getCause()); System.out.println("monitor_excep " + me); me.printStackTrace(); } }
public class OnMonitorCallback implements MqttCallback { public void connectionLost(Throwable cause) { // 连接丢失后,一般在这里面进行重连 System.out.println("连接断开,可以做重连"); }
public void messageArrived(String topic, MqttMessage message) throws Exception { String msg = new String(message.getPayload()); try { String result = new String(message.getPayload()); Map<String, String> resultMap2 = JsonUtil.jsonToMap(result); String clientId = String.valueOf(resultMap2.get("clientid")); if (topic.endsWith("disconnected")) { System.out.println("客户端已掉线:"+clientId); } else { System.out.println("客户端已上线:"+clientId); } } catch (JSONException e) { System.out.println("monitor_JSON Format Parsing Exception : "+msg); } }
public void deliveryComplete(IMqttDeliveryToken token) { System.out.println("deliveryComplete---------" + token.isComplete()); } }
三、修改配置文件
修改etc/acl.conf 如果担心其他人非法获取离线消息,可以设置订阅权限 设置allow所有用户订阅$SYS/brokers/+/clients/#主题
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
{allow, {ipaddr, "127.0.0.1"}, pubsub, ["$SYS/#", "#"]}.
{allow, all, subscribe, ["$SYS/brokers/+/clients/#"]}.
{deny, all, subscribe, ["$SYS/#", {eq, "#"}]}.
{allow, all}.