Rearchitected the broker, switching from ProtocolProcessor and publisher to MQTTConnection, Session and PostOffice

This commit is contained in:
andsel
2018-11-02 12:33:59 +01:00
parent 0ad6ffdd3c
commit a2266e5bbe
60 changed files with 5225 additions and 1025 deletions
@@ -15,6 +15,8 @@
*/
package io.moquette.spi.impl.subscriptions;
import io.netty.handler.codec.mqtt.MqttQoS;
import java.util.*;
class CNode {
@@ -79,8 +81,8 @@ class CNode {
this.children.remove(node);
}
CNode addSubscription(String clientId, Topic topic) {
this.subscriptions.add(new Subscription(clientId, topic));
CNode addSubscription(Subscription newSubscription) {
this.subscriptions.add(new Subscription(newSubscription));
return this;
}
@@ -98,9 +98,45 @@ public class CTrieSubscriptionDirectory implements ISubscriptionsDirectory {
* to use fo searching matching subscriptions.
* @return the list of matching subscriptions, or empty if not matching.
*/
Set<Subscription> match(Topic topic) {
final Set<Subscription> matchingSubs = recursiveMatch(topic, this.root);
@Override
public Set<Subscription> matchWithoutQosSharpening(Topic topic) {
return recursiveMatch(topic, this.root);
}
@Override
public Set<Subscription> matchQosSharpening(Topic topic) {
final Set<Subscription> subscriptions = matchWithoutQosSharpening(topic);
Map<String, Subscription> subsGroupedByClient = new HashMap<>();
for (Subscription sub : subscriptions) {
Subscription existingSub = subsGroupedByClient.get(sub.clientId);
// update the selected subscriptions if not present or if has a greater qos
if (existingSub == null || existingSub.qosLessThan(sub)) {
subsGroupedByClient.put(sub.clientId, sub);
}
}
return new HashSet<>(subsGroupedByClient.values());
}
/**
* Given a topic string return the clients subscriptions that matches it. Topic string can't
* contain character # and + because they are reserved to listeners subscriptions, and not topic
* publishing.
*
* @param topic
* to use fo searching matching subscriptions.
* @return the list of matching subscriptions, or empty if not matching.
*/
@Deprecated
Set<Subscription> match(Topic topic) {
final Set<Subscription> matchingSubs = matchWithoutQosSharpening(topic);
// remove the overlapping subscriptions, selecting ones with greatest qos
Map<String, Subscription> subsForClient = loadSubscriptionsFromStorage(matchingSubs);
return new HashSet<>(subsForClient.values());
}
@Deprecated
private Map<String, Subscription> loadSubscriptionsFromStorage(Set<Subscription> matchingSubs) {
Map<String, Subscription> subsForClient = new HashMap<>();
for (Subscription matchingSub : matchingSubs) {
Subscription existingSub = subsForClient.get(matchingSub.clientId);
@@ -120,7 +156,7 @@ public class CTrieSubscriptionDirectory implements ISubscriptionsDirectory {
subsForClient.put(sub.clientId, sub);
}
}
return new HashSet<>(subsForClient.values());
return subsForClient;
}
Set<Subscription> recursiveMatch(Topic topic, INode inode) {
@@ -171,28 +207,28 @@ public class CTrieSubscriptionDirectory implements ISubscriptionsDirectory {
public void add(Subscription newSubscription) {
Action res;
do {
res = insert(newSubscription.clientId, newSubscription.topicFilter, this.root, newSubscription.topicFilter);
res = insert(newSubscription.topicFilter, this.root, newSubscription);
} while (res == Action.REPEAT);
}
private Action insert(String clientId, Topic topic, final INode inode, Topic fullpath) {
private Action insert(Topic topic, final INode inode, Subscription newSubscription) {
Token token = topic.headToken();
if (!topic.isEmpty() && inode.mainNode().anyChildrenMatch(token)) {
Topic remainingTopic = topic.exceptHeadToken();
INode nextInode = inode.mainNode().childOf(token);
return insert(clientId, remainingTopic, nextInode, fullpath);
return insert(remainingTopic, nextInode, newSubscription);
} else {
if (topic.isEmpty()) {
return insertSubscription(clientId, fullpath, inode);
return insertSubscription(inode, newSubscription);
} else {
return createNodeAndInsertSubscription(clientId, topic, inode, fullpath);
return createNodeAndInsertSubscription(topic, inode, newSubscription);
}
}
}
private Action insertSubscription(String clientId, Topic topic, INode inode) {
private Action insertSubscription(INode inode, Subscription newSubscription) {
CNode cnode = inode.mainNode();
CNode updatedCnode = cnode.copy().addSubscription(clientId, topic);
CNode updatedCnode = cnode.copy().addSubscription(newSubscription);
if (inode.compareAndSet(cnode, updatedCnode)) {
return Action.OK;
} else {
@@ -200,8 +236,8 @@ public class CTrieSubscriptionDirectory implements ISubscriptionsDirectory {
}
}
private Action createNodeAndInsertSubscription(String clientId, Topic topic, INode inode, Topic fullpath) {
INode newInode = createPathRec(clientId, topic, fullpath);
private Action createNodeAndInsertSubscription(Topic topic, INode inode, Subscription newSubscription) {
INode newInode = createPathRec(topic, newSubscription);
CNode cnode = inode.mainNode();
CNode updatedCnode = cnode.copy();
updatedCnode.add(newInode);
@@ -209,24 +245,24 @@ public class CTrieSubscriptionDirectory implements ISubscriptionsDirectory {
return inode.compareAndSet(cnode, updatedCnode) ? Action.OK : Action.REPEAT;
}
private INode createLeafNodes(String clientId, Topic fullpath, Token token) {
private INode createLeafNodes(Token token, Subscription newSubscription) {
CNode newLeafCnode = new CNode();
newLeafCnode.token = token;
newLeafCnode.addSubscription(clientId, fullpath);
newLeafCnode.addSubscription(newSubscription);
return new INode(newLeafCnode);
}
private INode createPathRec(String clientId, Topic topic, Topic fullpath) {
private INode createPathRec(Topic topic, Subscription newSubscription) {
Topic remainingTopic = topic.exceptHeadToken();
if (!remainingTopic.isEmpty()) {
INode inode = createPathRec(clientId, remainingTopic, fullpath);
INode inode = createPathRec(remainingTopic, newSubscription);
CNode cnode = new CNode();
cnode.token = topic.headToken();
cnode.add(inode);
return new INode(cnode);
} else {
return createLeafNodes(clientId, fullpath, topic.headToken());
return createLeafNodes(topic.headToken(), newSubscription);
}
}
@@ -18,11 +18,16 @@ package io.moquette.spi.impl.subscriptions;
import io.moquette.spi.impl.SessionsRepository;
import java.util.List;
import java.util.Set;
public interface ISubscriptionsDirectory {
void init(SessionsRepository sessionsRepository);
Set<Subscription> matchWithoutQosSharpening(Topic topic);
Set<Subscription> matchQosSharpening(Topic topic);
void add(Subscription newSubscription);
void removeSubscription(Topic topic, String clientID);
@@ -33,7 +33,7 @@ class TNode extends CNode {
}
@Override
CNode addSubscription(String clientId, Topic topic) {
CNode addSubscription(Subscription newSubscription) {
throw new IllegalStateException("Can't be invoked on TNode");
}