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
+8
View File
@@ -1,9 +1,17 @@
description = 'Moquette - performance tests'
repositories {
jcenter()
mavenCentral()
maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local' }
}
dependencies {
compile project(':moquette-broker')
compile group: 'io.netty', name: 'netty-codec-mqtt', version:"${nettyVersion}"
compile group: 'org.hdrhistogram', name: 'HdrHistogram', version:'2.1.2'
compile group: 'org.eclipse.paho', name: 'org.eclipse.paho.client.mqttv3', version:'1.2.0'
compile group: 'org.eclipse.jetty.toolchain', name: 'jetty-perf-helper', version:'1.0.5'
compile(group: 'org.mqttbee', name: 'mqtt-bee', version: '1.0.0-SNAPSHOT')
}
@@ -0,0 +1,100 @@
package io.moquette.performance.clients.mqttbee;
import io.reactivex.Flowable;
import io.reactivex.Single;
import org.mqttbee.api.mqtt.MqttClient;
import org.mqttbee.api.mqtt.datatypes.MqttQos;
import org.mqttbee.api.mqtt.mqtt3.Mqtt3Client;
import org.mqttbee.api.mqtt.mqtt3.message.connect.connack.Mqtt3ConnAck;
import org.mqttbee.api.mqtt.mqtt3.message.publish.Mqtt3Publish;
import org.mqttbee.api.mqtt.mqtt3.message.publish.Mqtt3PublishResult;
import org.mqttbee.api.mqtt.mqtt3.message.subscribe.suback.Mqtt3SubAck;
import org.mqttbee.rx.FlowableWithSingle;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class BeeBenchmarker {
public static void main(String[] args) {
final Mqtt3Client client = MqttClient.builder()
.identifier(UUID.randomUUID().toString())
.serverHost("localhost")
.serverPort(1883)
.useMqttVersion3()
.buildReactive();
/*
* As we use the reactive API, the following line does not connect yet, but return a reactive type.
* e.g. Single is something like a lazy and reusable future.
*/
final Single<Mqtt3ConnAck> connAckSingle = client.connect().keepAlive(10, TimeUnit.SECONDS).done();
// /*
// * Same here: the following line does not subscribe yet, but return a reactive type.
// * FlowableWithSingle is a combination of the single SUBACK message and a Flowable of PUBLISH messages.
// * A Flowable is an asynchronous stream, that enables back pressure from the application over MQTT Bee to the broker.
// */
// final FlowableWithSingle<Mqtt3SubAck, Mqtt3Publish> subAckAndMatchingPublishes = client.subscribeWithStream()
// .addSubscription().topicFilter("a/b/c").qos(MqttQos.AT_LEAST_ONCE).done()
// .addSubscription().topicFilter("a/b/c/d").qos(MqttQos.EXACTLY_ONCE).done()
// .done();
//
// /*
// * The reactive types offer as many operators that will not be covered here.
// * Here we register simple callbacks to print messages when we received the CONNACK, SUBACK and matching PUBLISH messages.
// */
// final Single<Mqtt3ConnAck> connectScenario = connAckSingle
// .doOnSuccess(connAck -> System.out.println("Connected with return code " + connAck.getReturnCode()))
// .doOnError(throwable -> System.out.println("Connection failed, " + throwable.getMessage()));
//
// final Flowable<Mqtt3Publish> subscribeScenario = subAckAndMatchingPublishes
// .doOnSingle((subAck, subscription) ->
// System.out.println("Subscribed with return codes " + subAck.getReturnCodes()))
// .doOnNext(publish ->
// System.out.println("Received publish" +
// ", topic: " + publish.getTopic() +
// ", QoS: " + publish.getQos() +
// ", payload: " + new String(publish.getPayloadAsBytes())
// )
// );
//
// /*
// * Reactive types let us compose a sequence of actions
// */
// final Flowable<Mqtt3Publish> connectAndSubscribe = connectScenario.toCompletable().andThen(subscribeScenario);
//
// /*
// * By subscribing to reactive types, the sequence is executed
// */
// connectAndSubscribe.blockingSubscribe();
/*
* Fake a stream of PUBLISH messages to publish with incrementing ids
*/
final AtomicInteger counter = new AtomicInteger();
Flowable<Mqtt3Publish> messagesToPublish = Flowable.generate(emitter -> {
final int id = counter.incrementAndGet();
final Mqtt3Publish publishMessage = Mqtt3Publish.builder()
.topic("a/b/c").qos(MqttQos.AT_LEAST_ONCE).payload(("test " + id).getBytes()).build();
emitter.onNext(publishMessage);
});
/*
* Emit 1 message only every 100 milliseconds
*/
messagesToPublish = messagesToPublish.zipWith(Flowable.interval(10, TimeUnit.MILLISECONDS), (publish, aLong) -> publish);
final Single<Mqtt3ConnAck> connectScenario = connAckSingle
.doOnSuccess(connAck -> System.out.println("Connected with return code " + connAck.getReturnCode()))
.doOnError(throwable -> System.out.println("Connection failed, " + throwable.getMessage()));
final Flowable<Mqtt3PublishResult> publishScenario = client.publish(messagesToPublish)
.doOnNext(publishResult -> System.out.println("Publish acknowledged: " + new String(publishResult.getPublish().getPayloadAsBytes())));
connectScenario.toCompletable().andThen(publishScenario).blockingSubscribe();
}
}
@@ -0,0 +1,56 @@
package io.moquette.performance.clients.paho;
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import java.nio.ByteBuffer;
public class PahoBenchmarker {
private static void publishQos0(IMqttAsyncClient client, long numMessages) throws MqttException {
System.out.println("publishing..");
long startTime = System.currentTimeMillis();
for (int i = 0; i < numMessages; i++) {
byte[] bytes = ByteBuffer.allocate(8).putLong(System.currentTimeMillis()).array();
client.publish("topic", bytes, 0, false);
}
client.publish("/exit", "Exit".getBytes(), 0, false);
long stopTime = System.currentTimeMillis();
long spentTime = stopTime -startTime;
System.out.println("published in " + spentTime + " ms");
}
public static void main(String[] args) throws MqttException, InterruptedException {
if (args.length < 2) {
System.out.println("Usage benchmarker <host> <numMessages>");
System.out.println("java io.moquette.performance.clients.paho.PahoBenchmarker localhost 1000 500 test1");
return;
}
String host = args[0];
long numMessages = Long.parseLong(args[1]);
String tmpDir = System.getProperty("java.io.tmpdir");
MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir);
//MemoryPersistence dataStore = new MemoryPersistence()
int rnd = (int) (Math.random() * 100);
MqttAsyncClient client = new MqttAsyncClient("tcp://" + host +":1883",
"SubscriberClient" + rnd, dataStore);
SubscriberCallback callback = new SubscriberCallback();
client.setCallback(callback);
client.connect().waitForCompletion(1000);
client.subscribe("topic", 0);
System.out.println("subscribed to topic");
client.subscribe("/exit", 0);
System.out.println("subscribed to /exit");
publishQos0(client, numMessages);
callback.waitFinish();
// Map sorted = callback.distribution.sort( { k1, k2 -> k1 <=> k2 } as Comparator)
// File csv = new File("perf.csv")
// sorted.each {delay, count -> csv << "${delay}, ${count}\r\n"}
}
}
@@ -0,0 +1,63 @@
package io.moquette.performance.clients.paho;
import org.eclipse.paho.client.mqttv3.*;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
class SubscriberCallback implements MqttCallback {
int m_numReceived = 0;
long m_startTime;
boolean firstMessageReceived = false;
private CountDownLatch m_latch = new CountDownLatch(1);
//Maps delay to num of messages reached with that delay
protected Map<Long, Long> distribution = new HashMap<>();
IMqttAsyncClient client;
void waitFinish() throws InterruptedException {
m_latch.await();
}
public void connectionLost(Throwable cause) {
m_latch.countDown();
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
//println "Received message ${new String(message.payload)} on topic [${topic}]"
if (!firstMessageReceived) {
m_startTime = System.currentTimeMillis();
firstMessageReceived = true;
}
if (topic == "/exit") {
long stopTime = System.currentTimeMillis();
long spentTime = stopTime - m_startTime;
System.out.println("/exit received");
System.out.println("subscriber disconnected, received ${m_numReceived} messages in ${spentTime} ms (from first received to last one on topic /exit)");
client.disconnect();
m_latch.countDown();
} else {
ByteBuffer bb = ByteBuffer.wrap(message.getPayload());
long sentTime = bb.getLong();
long delay = System.currentTimeMillis() - sentTime;
//println "-received in ${delay}(ms) on ${topic} with QoS ${message.qos}"
//distribution[delay] = distribution[delay] != null ? distribution[delay]++ : 1
Long value = distribution.get(delay);
if (value != null) {
distribution.put(delay, value++);
} else {
distribution.put(delay, 1L);
}
m_numReceived++;
}
}
public void deliveryComplete(IMqttDeliveryToken token) {
//To change body of implemented methods use File | Settings | File Templates.
}
}