EMQ共享订阅

1 共享订阅

多个客户端订阅了同一个主题,发布者发布主题时,每个客户端都会同时收到这个主题的消息。在客户端集群部署的场景下会出现消息重复处理的问题。
EMQ支持共享订阅,多个客户端订阅了同一个主题,发布者发布主题时,只有其中一个客户端接收到消息。
共享订阅有两种方式:
(1)共享订阅:订阅前缀$queue/
多个客户端订阅了$queue/topic,发布者发布到topic,则只有一个客户端会接收到消息。
(2)分组订阅:订阅前缀$share/<group>/
多组客户端订阅了$queue/group1/topic、$queue/group2/topic...,发布者发布到topic,则消息会发布到每个group中,但是每个group中只有一个客户端会接收到消息。

2 Java客户端实现共享订阅

开发时发现,使用eclipse paho java客户端时,无法处理共享订阅。订阅$queue/topic能够订阅成功,并且跟踪代码能看到emq也把消息转发到了客户端,但是客户端丢弃掉了。
解决方法就是重写mqtt的回调函数,实现MqttCallback接口。

 

实现MqttCallback接口的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

package com.emqtest.emqtest;

 

 

import java.util.HashMap;

import java.util.Map;

 

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;

import org.eclipse.paho.client.mqttv3.IMqttMessageListener;

import org.eclipse.paho.client.mqttv3.MqttCallback;

import org.eclipse.paho.client.mqttv3.MqttMessage;

import org.eclipse.paho.client.mqttv3.MqttTopic;

 

public class SharedSubCallbackRouter implements MqttCallback {

    private Map<String, IMqttMessageListener> topicFilterListeners;

 

    public SharedSubCallbackRouter(Map<String, IMqttMessageListener> topicFilterListeners) {

        this.topicFilterListeners = topicFilterListeners;

    }

 

    public void addSubscriber(String topicFilter, IMqttMessageListener listener) {

        if (this.topicFilterListeners == null) {

             this.topicFilterListeners = new HashMap<>();

        }

        this.topicFilterListeners.put(topicFilter, listener);

    }

 

    @Override

    public void connectionLost(Throwable cause) {

 

    }

 

    @Override

    public void messageArrived(String topic, MqttMessage message) throws Exception {

        for (Map.Entry<String, IMqttMessageListener> listenerEntry : topicFilterListeners.entrySet()) {

            String topicFilter = listenerEntry.getKey();

            if (isMatched(topicFilter, topic)) {

                listenerEntry.getValue().messageArrived(topic, message);

            }

        }

    }

 

    @Override

    public void deliveryComplete(IMqttDeliveryToken token) {

 

    }

 

    /**

     * Paho topic matcher does not work with shared subscription topic filter of emqttd

     * https://github.com/eclipse/paho.mqtt.java/issues/367#issuecomment-300100385

     * <p>

     * http://emqtt.io/docs/v2/advanced.html#shared-subscription

     *

     * @param topicFilter the topicFilter for mqtt

     * @param topic       the topic

     * @return boolean for matched

     */

    private boolean isMatched(String topicFilter, String topic) {

        if (topicFilter.startsWith("$queue/")) {

            topicFilter = topicFilter.replaceFirst("\\$queue/""");

        else if (topicFilter.startsWith("$share/")) {

            topicFilter = topicFilter.replaceFirst("\\$share/""");

            topicFilter = topicFilter.substring(topicFilter.indexOf('/'));

        }

        return MqttTopic.isMatched(topicFilter, topic);

    }

}

 

创建emq连接代码如下:

1

2

3

4

5

6

7

mqttClient = new MqttClient("tcp://localhost:1883""MqttClient");

mqttClient.connect();

Map<String, IMqttMessageListener> listeners = new HashMap<>();

IMqttMessageListener emqListener = new EmqListener();

listeners.put("$queue/testmqtt", emqListener);

mqttClient.setCallback(new SharedSubCallbackRouter(listeners));

mqttClient.subscribe("$queue/testmqtt"new EmqListener());

 

还要再写一个实现IMqttMessageListener接口的Emq消息处理类:

1

2

3

4

5

6

7

8

9

10

11

12

@Component

public class EmqListener implements IMqttMessageListener {

 

  @Override

  public void messageArrived(String topic, MqttMessage message) throws Exception {

    try {

      System.out.println("topic: " + topic);

    catch (Exception e) {

      e.printStackTrace();

    }

  }

}

 

 

 

 

参考链接:

1 emq的github上关于这个问题的讨论:
https://github.com/emqx/emqx/issues/921#event-1023359646
2 网上有人给的一个解决方法示例代码:
https://github.com/yogin16/paho-shared-sub-example
3 eclipse paho的github链接:
https://github.com/eclipse/paho.mqtt.java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Java中使用emqx进行订阅和发布16进制数据,需要使用MQTT客户端库。以下是一个简单的示例代码,它将以16进制格式发布一个字节数组,并订阅一个主题以接收16进制格式的消息。 ```java import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; public class MqttExample { public static void main(String[] args) throws MqttException { String broker = "tcp://localhost:1883"; String clientId = "JavaExample"; MemoryPersistence persistence = new MemoryPersistence(); // 创建MQTT客户端实例 MqttClient client = new MqttClient(broker, clientId, persistence); // 设置回调函数 client.setCallback(new MqttCallback() { public void connectionLost(Throwable cause) {} public void messageArrived(String topic, MqttMessage message) throws Exception { // 接收到消息时执行的代码 byte[] payload = message.getPayload(); String hexString = bytesToHexString(payload); System.out.println("Received message: " + hexString); } public void deliveryComplete(IMqttDeliveryToken token) {} }); // 连接到MQTT代理服务器 client.connect(); // 订阅主题 String topic = "test"; client.subscribe(topic); // 发布消息 byte[] payload = {0x01, 0x02, 0x03}; MqttMessage message = new MqttMessage(payload); message.setQos(0); client.publish(topic, message); // 断开连接 client.disconnect(); } // 将字节数组转换为16进制字符串 public static String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X", b)); } return sb.toString(); } } ``` 在上面的示例中,`bytesToHexString`方法将字节数组转换为16进制字符串。在订阅的回调函数中,接收到的消息将转换为16进制字符串并打印到控制台上。在发布消息时,字节数组将作为`MqttMessage`对象的负载,并使用`client.publish`方法发布到指定的主题上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值