Rearchitected the broker, switching from ProtocolProcessor and publisher to MQTTConnection, Session and PostOffice
This commit is contained in:
@@ -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.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user