La identificación de un cuello de botella en un editor MQTT multihilo

Radioo:

Actualmente estoy desarrollando un servicio al cliente MQTT con Eclipse OPS para un software más grande y me encuentro con problemas de rendimiento. Me estoy poniendo un montón de eventos que desea publicar en el corredor y estoy usando GSON para la serialización de esos eventos. He multiproceso la serialización y la edición. De acuerdo con una serialización de referencia rudimentaria y la publicación de toma hasta 1 ms. Estoy usando un ExecutorService con un threadpool tamaño fijo de 10 (por ahora).

Mi código se somete actualmente a unos 50 Runnables por segundo a la ExecutorService, pero mi agente informa solamente unos 5-10 mensajes por segundo. He referenciados anteriormente mi configuración MQTT y logró enviar mensajes acerca 9000+ MQTT por segundo en una forma no multiproceso.

¿El subprocesos tienen que gran parte de una sobrecarga, que yo sólo soy capaz de conseguir esta pequeña cantidad de publica fuera de él?

public class MqttService implements IMessagingService{
    protected int PORT = 1883;
    protected String HOST = "localhost";
    protected final String SERVICENAME = "MQTT";
    protected static final String COMMANDTOPIC = "commands";
    protected static final String REMINDSPREFIX = "Reminds/";
    protected static final String VIOLATIONTOPIC = "violations/";
    protected static final String WILDCARDTOPIC = "Reminds/#";
    protected static final String TCPPREFIX = "tcp://";
    protected static final String SSLPREFIX = "ssl://";

    private MqttClient client;
    private MqttConnectOptions optionsPublisher = new MqttConnectOptions();

    private ExecutorService pool = Executors.newFixedThreadPool(10);

    public MqttService() {
        this("localhost", 1883);
    }

    public MqttService(String host, int port) {
        this.HOST = host;
        this.PORT = port;

    }

    @Override
    public void setPort(int port) {
        this.PORT = port;
    }

    @Override
    public void setHost(String host) {
        this.HOST = host;
    }

    @Override
    public void sendMessage(AbstractMessage message) {
        pool.submit(new SerializeJob(client,message));
    }

    @Override
    public void connect() {
        try {
            client = new MqttClient(TCPPREFIX + HOST + ":" + PORT, IDPublisher);
            optionsPublisher.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
            client.connect(optionsPublisher);
            client.setCallback(new MessageCallback());
            client.subscribe(WILDCARDTOPIC, 0);
        } catch (MqttException e1) {
            e1.printStackTrace();
        }
    }
}

El código siguiente es el Ejecutable ejecutado por el ExecutorService. Esto no debería ser un problema por sí mismo, sin embargo, ya que sólo tarda hasta 1-2 ms a fin.

class SerializeJob implements Runnable {
    private AbstractMessage message;
    private MqttClient client;

    public SerializeJob(MqttClient client, AbstractMessage m) {
        this.client = client;
        this.message = m;
    }

    @Override
    public void run() {
        String serializedMessage = MessageSerializer.serializeMessage(message);
        MqttMessage wireMessage = new MqttMessage();
        wireMessage.setQos(message.getQos());
        wireMessage.setPayload(serializedMessage.getBytes());
        if (client.isConnected()) {
            StringBuilder topic = new StringBuilder();
            topic.append(MqttService.REMINDSPREFIX);
            topic.append(MqttService.VIOLATIONTOPIC);
            try {
                client.publish(topic.toString(), wireMessage);
            } catch (MqttPersistenceException e) {
                e.printStackTrace();
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }
    }

}

No estoy muy seguro de lo que me está embocar espalda. MQTT sí parece permitir una gran cantidad de eventos, que también pueden tener una gran carga útil, y la red no es posible que sea un problema, ya que estoy actualmente aloja el agente localmente en la misma máquina que el cliente.

Edición con más pruebas:

sintéticamente He Benchmarked ahora mi propia configuración, que consistía en un corredor HiveMQ y mosquitto anfitrión local que funcionó "nativa" de la máquina. El uso de las bibliotecas Paho He enviado mensajes cada vez más grandes en lotes de 1000. Para cada lote que se calcula el rendimiento en los mensajes desde el primero al último mensaje. Este escenario no utilizó ningún multihilo. Con esto se me ha ocurrido con la siguiente tabla de rendimiento:

Rendimiento para los mensajes MQTT de diferentes tamaños con 2 corredores de diferentes

La máquina que ejecuta el cliente y los corredores es un escritorio con un i7 6700 y 32 GB de RAM. Los corredores tuvieron acceso a todos los núcleos y 8 GB de memoria para su máquina virtual.

Para la evaluación comparativa He utilizado el siguiente código:

import java.util.Random;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;

public class MqttBenchmarker {
    protected static int PORT = 1883;
    protected static String HOST = "localhost";
    protected final String SERVICENAME = "MQTT";
    protected static final String COMMANDTOPIC = "commands";
    protected static final String REMINDSPREFIX = "Reminds/";
    protected static final String VIOLATIONTOPIC = "violations/";
    protected static final String WILDCARDTOPIC = "Reminds/#";
    protected static final String TCPPREFIX = "tcp://";
    protected static final String SSLPREFIX = "ssl://";

    private static MqttClient client;
    private static MqttConnectOptions optionsPublisher = new MqttConnectOptions();
    private static String IDPublisher = MqttClient.generateClientId();

    private static int messageReceived = 0;
    private static long timesent = 0;
    private static int count = 2;
    private static StringBuilder out = new StringBuilder();
    private static StringBuilder in = new StringBuilder();
    private static final int runs = 1000;
    private static boolean receivefinished = false;

    public static void main(String[] args) {
        connect();
        Thread sendThread=new Thread(new Runnable(){

            @Override
            public void run() {
                Random rd = new Random();
                for (int i = 2; i < 1000000; i += i) {
                    byte[] arr = new byte[i];
                    // System.out.println("Starting test run for byte Array of size:
                    // "+arr.length);
                    long startt = System.currentTimeMillis();
                    System.out.println("Test for size: " + i + " started.");
                    for (int a = 0; a <= runs; a++) {

                        rd.nextBytes(arr);
                        try {
                            client.publish(REMINDSPREFIX, arr, 1, false);
                        } catch (MqttPersistenceException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (MqttException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    try {
                        while (!receivefinished) {
                            Thread.sleep(10);
                        }
                        receivefinished = false;
                        System.out.println("Test for size: " + i + " finished.");
                        out.append("Sending Payload size: " + arr.length + " achieved "
                                + runs / ((System.currentTimeMillis() - startt) / 1000d) + " messages per second.\n");

                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println(out.toString());
                System.out.println(in.toString());
            }

        });
        sendThread.start();

    }

    private static class MessageCallback implements MqttCallback {

        @Override
        public void messageArrived(String arg0, MqttMessage arg1) throws Exception {
            if (messageReceived == 0) {
                timesent = System.currentTimeMillis();
            }
            messageReceived++;
            if (messageReceived >= runs) {
                receivefinished = true;
                in.append("Receiving payload size " + count + " achieved "
                        + runs / ((System.currentTimeMillis() - timesent) / 1000d) + " messages per second.\n");
                count += count;
                messageReceived = 0;
            }
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void connectionLost(Throwable arg0) {
            // TODO Auto-generated method stub

        }
    }

    public static void connect() {
        try {
            client = new MqttClient(TCPPREFIX + HOST + ":" + PORT, IDPublisher);
            optionsPublisher.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
            optionsPublisher.setAutomaticReconnect(true);
            optionsPublisher.setCleanSession(false);
            optionsPublisher.setMaxInflight(65535);
            client.connect(optionsPublisher);
            while (!client.isConnected()) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            client.setCallback(new MessageCallback());
            client.subscribe(WILDCARDTOPIC, 0);
        } catch (MqttException e1) {
            e1.printStackTrace();
        }
    }

}


Lo raro es, que los mensajes serializados que desea enviar desde mi aplicación sólo utiliza aproximadamente 4000 bytes. Por lo que el rendimiento teórico debe ser alrededor de 200 mensajes por segundo. ¿Podría ser esto un problema causado por los cálculos más largos dentro de la función de devolución de llamada? Yo ya he logrado resultados mucho mejores con el corredor mosquitto, y voy a realizar pruebas adicionales, hasta dónde puedo empujar el rendimiento de la misma.

Gracias por cualquier sugerencia!

Suroter:

Uno de los problemas es en el sistema de prueba del cliente MQTT.

Está utilizando sólo un único cliente MQTT. Lo que se está probando con eficacia es el tamaño de la ventana durante el vuelo MQTT con esta fórmula:

  throughput <= inflight window-size / round-trip time

HiveMQ trae por defecto una propiedad permitido que se llama <cluster-overload-protection>que los límites de la inflight windowde un solo cliente.

Además el cliente OPS no está muy adecuado para el trabajo de alto rendimiento en un contexto multi-hilos. Una mejor aplicación para escenarios de alto rendimiento sería el HiveMQ MQTT cliente .

Con 20 clientes conectados (10 Publicación y 10 de recepción), alcanzo un rendimiento sostenido de alrededor de 6.000 qos = 1 10kb mensajes por segundo.

introducir descripción de la imagen aquí

Exención de responsabilidad: trabajo que como desarrollador de software para HiveMQ.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=231505&siteId=1
Recomendado
Clasificación