5
votes

Currently we have an app that controls multiple devices through MQTT. Each device subscribes to a unique topic which is named after their device ID. For example, device A has device ID 123 so it will subscribe to topic 123. Then if the app wants to publish control message to device A, then it will publish a topic named 123, which is the device ID of device A.

By doing this, if we have 1000 devices then we will have 1000 topics. This is a really bad design. So we are thinking that maybe we can publish a topic to a specific client by setting the client ID that will receive the topic since each client that connects to the broker will have to set a client ID. However, we did not find any method that allows publishing to a specific client. And it seems that MQTT doesn't handle such thing. It only publishes to clients subscribing to the same topic.

So, is there any other way that we can do to to achieve one topic, but still able to publish message to specific clients?

Thanks!

2
"This is a really bad design". Why do you think so? Topics are cheap. 1000 topics will not eat 1000x memory anywhere.Pavel Zdenek
@PavelZdenek Really? I just thought so because I think it would take up a lot of memory if there comes more devices. Of course 1000 is a really small number, but what if millions or even more. I am not really that sure though.JLT
Well if you subscribe millions of devices to one broker, it will "take up a lot of memory" for sure. But topic specificity is just a small part of the memory. Scalability is solved by broker clustering/bridging, not by using unspecific topics. If you plan for such large scale operation, you should start putting partitions or "area codes" in front of your client topics.Pavel Zdenek

2 Answers

16
votes

There is no way to publish a message to a single subscriber at the MQTT protocol level.

One of the key tenets of a publish/subscribe system is to totally decouple the publisher from the subscribers, there is no way for a publisher to know if there are any subscribers to a given topic let alone target one specifically.

Using a topic for each device is not a problem as there is virtually no overhead in the broker for each topic. You can also use ACLs to ensure that each client can only subscribe their own topic (while still being able to publish to others if needed)

You could use a single topic that all clients subscribe to and encode the target device in the payload and have the device decide if the message is for it's self. One downside to this is that you can't apply ACLs to this model. Another downside is increased network traffic, since uninteresting messages will be sent to many subscribers.

1
votes

We have managed this within the Mosca MQTT Broker, that is not on protocoll level, but it works fine, by a procedure that the clients send in the course of the subscription a message, containing an options, which controls the forwarding of logging (channel "logger") messages

{"login":{"UserName":"John_Doe","option":["receive_logs"]}}

Retrieve login information and

let LoggedInUsers = {};
server.on('published', function(packet, client) {
    let contentstr = packet.payload.toString();

    //parse Package Content
    let packagecontent = JSON.parse(contentstr);

    if (packagecontent.hasOwnProperty('login'))
        if (!LoggedInUsers.hasOwnProperty(client.id))
            LoggedInUsers[client.id] = packagecontent.login;
}

Provide an individual "authorizeForward", checking if the user is supposed to receive the relevent contents

let authorizeForward = function(client, packet, callback) {
    if (packet.topic === "logger" && LoggedInUsers.hasOwnProperty(client.id))
        if (LoggedInUsers[client.id].option.indexOf('receive_logs') === -1)
             return;

    callback(null, true);
};

The next step will be to request the data from a server and read the rights from there...