API Java de RocketMQ

、 、 API de Java

Descripción:

  • La versión del servidor RocketMQ es actualmente la última versión: 4.7.0
  • La última versión actual adoptada por la versión del cliente Java: 4.7.0
  • pom es el siguiente
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.7.0</version>
</dependency>

1 、 Productor

Las siguientes condiciones deben ser necesarias para enviar un mensaje:

  • Especifique el nombre del grupo de producción (no se puede usar el predeterminado, se informará un error)

  • Configurar la dirección de namesrv (obligatorio)

  • Especifique el nombre del tema (obligatorio)

  • Especificar etiqueta / clave (opcional)

Verifique que el mensaje se envíe con éxito: después de que se envíe el mensaje, puede iniciar el consumidor para el consumo, o puede ir a la consola de control para ver si el mensaje existe.

1.1, enviar (sincronización)

public class Producer {
    public static void main(String[] args) throws Exception {
        // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();
        // 创建消息对象,topic为:myTopic001,消息内容为:hello world
        Message msg = new Message("myTopic001", "hello world".getBytes());
        // 发送消息到mq,同步的
        SendResult result = producer.send(msg);
        System.out.println("发送消息成功!result is : " + result);
        // 关闭Producer
        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}

Resultado de salida:

发送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854140F418B4AAC26F7973910000, offsetMsgId=7B39B49D00002A9F00000000000589BE, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=0], queueOffset=7]
生产者 shutdown!

1.2, enviar (lote)

public class ProducerMultiMsg {
    public static void main(String[] args) throws Exception {
        // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();

        String topic = "myTopic001";
        // 创建消息对象,topic为:myTopic001,消息内容为:hello world1/2/3
        Message msg1 = new Message(topic, "hello world1".getBytes());
        Message msg2 = new Message(topic, "hello world2".getBytes());
        Message msg3 = new Message(topic, "hello world3".getBytes());
		// 创建消息对象的集合,用于批量发送
        List<Message> msgs = new ArrayList<>();
        msgs.add(msg1);
        msgs.add(msg2);
        msgs.add(msg3);
        // 批量发送的api的也是send(),只是他的重载方法支持List<Message>,同样是同步发送。
        SendResult result = producer.send(msgs);
        System.out.println("发送消息成功!result is : " + result);
        // 关闭Producer
        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}

Resultado de salida:

发送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854139C418B4AAC26F7D13770000,A9FE854139C418B4AAC26F7D13770001,A9FE854139C418B4AAC26F7D13770002, offsetMsgId=7B39B49D00002A9F0000000000058A62,7B39B49D00002A9F0000000000058B07,7B39B49D00002A9F0000000000058BAC, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=0], queueOffset=8]
生产者 shutdown!

Se puede ver en el resultado que solo hay un msgId, por lo que se puede encontrar que aunque hay tres objetos de mensaje, solo se envían una vez, lo que ahorra en gran medida la sobrecarga del cliente y el servidor.

Condiciones de error:

Los temas a enviar en lotes deben ser los mismos. Si el objeto de mensaje especifica diferentes temas, se informará un error al enviar en lotes:

Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: Failed to initiate the MessageBatch
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
	at org.apache.rocketmq.client.producer.DefaultMQProducer.batch(DefaultMQProducer.java:950)
	at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:898)
	at com.chentongwei.mq.rocketmq.ProducerMultiMsg.main(ProducerMultiMsg.java:29)
Caused by: java.lang.UnsupportedOperationException: The topic of the messages in one batch should be the same
	at org.apache.rocketmq.common.message.MessageBatch.generateFromList(MessageBatch.java:58)
	at org.apache.rocketmq.client.producer.DefaultMQProducer.batch(DefaultMQProducer.java:942)
	... 2 more

1.3, sendCallBack (asincrónico)

public class ProducerASync {
    public static void main(String[] args) throws Exception {
       // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();
        
        // 创建消息对象,topic为:myTopic001,消息内容为:hello world async
        Message msg = new Message("myTopic001", "hello world async".getBytes());
        // 进行异步发送,通过SendCallback接口来得知发送的结果
        producer.send(msg, new SendCallback() {
            // 发送成功的回调接口
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("发送消息成功!result is : " + sendResult);
            }
			// 发送失败的回调接口
            @Override
            public void onException(Throwable throwable) {
                throwable.printStackTrace();
                System.out.println("发送消息失败!result is : " + throwable.getMessage());
            }
        });

        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}

Resultado de salida:

生产者 shutdown!
java.lang.IllegalStateException: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed
	at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:681)
	at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:511)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.tryToFindTopicPublishInfo(DefaultMQProducerImpl.java:692)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:556)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.access$300(DefaultMQProducerImpl.java:97)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl$4.run(DefaultMQProducerImpl.java:510)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed
	at org.apache.rocketmq.remoting.netty.NettyRemotingClient.getAndCreateNameserverChannel(NettyRemotingClient.java:441)
	at org.apache.rocketmq.remoting.netty.NettyRemotingClient.getAndCreateChannel(NettyRemotingClient.java:396)
	at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:365)
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.getTopicRouteInfoFromNameServer(MQClientAPIImpl.java:1371)
	at org.apache.rocketmq.client.impl.MQClientAPIImpl.getTopicRouteInfoFromNameServer(MQClientAPIImpl.java:1361)
	at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:624)
	... 10 more
发送消息失败!result is : org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed

¿Por qué informó un error? Es muy simple. Es asincrónico. Se puede ver por el resultado. Debido a que es asincrónico, aún no lo he enviado a mq, por lo que primero debe apagarlo. Ciertamente no, así que dormimos 1 s antes del apagado para ver el efecto

public class ProducerASync {
    public static void main(String[] args) throws Exception {
       // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();
        
        // 创建消息对象,topic为:myTopic001,消息内容为:hello world async
        Message msg = new Message("myTopic001", "hello world async".getBytes());
        // 进行异步发送,通过SendCallback接口来得知发送的结果
        producer.send(msg, new SendCallback() {
            // 发送成功的回调接口
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("发送消息成功!result is : " + sendResult);
            }
			// 发送失败的回调接口
            @Override
            public void onException(Throwable throwable) {
                throwable.printStackTrace();
                System.out.println("发送消息失败!result is : " + throwable.getMessage());
            }
        });
        
        Thread.sleep(1000);

        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}
  • Resultado de salida:
发送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854106E418B4AAC26F8719B20000, offsetMsgId=7B39B49D00002A9F0000000000058CFC, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=1], queueOffset=2]
生产者 shutdown!

1.4 、 enviarOneway

public class ProducerOneWay {
    public static void main(String[] args) throws Exception {
        // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();
        
		// 创建消息对象,topic为:myTopic001,消息内容为:hello world oneway
        Message msg = new Message("myTopic001", "hello world oneway".getBytes());
        // 效率最高,因为oneway不关心是否发送成功,我就投递一下我就不管了。所以返回是void
        producer.sendOneway(msg);
        System.out.println("投递消息成功!,注意这里是投递成功,而不是发送消息成功哦!因为我sendOneway也不知道到底成没成功,我没返回值的。");
        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}

Resultado de salida:

投递消息成功!,注意这里是投递成功,而不是发送消息成功哦!因为我sendOneway也不知道到底成没成功,我没返回值的。
生产者 shutdown!

1.5, comparación de eficiencia

sendOneway> sendCallBack> enviar lote> enviar sencillo

Es fácil de entender que sendOneway no pide resultados, yo seré el responsable de la entrega. No me importa si fallas o triunfas, es equivalente a una estación de transferencia. Cuando vengas, la tiraré. Yo no realizará ningún otro procesamiento. Entonces el más rápido.

Y sendCallBack es definitivamente más eficiente que el envío síncrono.

La eficiencia del envío por lotes y el envío único también se divide en diferentes situaciones: si solo hay un mensaje para enviar, el lote se sigue utilizando y el envío único se realiza directamente.

2 、 Consumidor

Cada consumidor solo puede seguir un tema.

Las siguientes condiciones deben ser necesarias para enviar un mensaje:

  • Especifique el nombre del grupo de consumidores (no se puede usar el predeterminado, se informará un error)

  • Configurar la dirección de namesrv (obligatorio)

  • Especifique el nombre del tema (obligatorio)

  • Especificar etiqueta / clave (opcional)

2.1 、 AGRUPACIÓN

Modo de clúster, el predeterminado.

Por ejemplo, después de iniciar cinco Consumidores y el Productor produce un mensaje, el Broker seleccionará uno de los cinco Consumidores para consumir el mensaje, por lo que pertenece a un modelo de consumo punto a punto.

public class Consumer {
    public static void main(String[] args) throws Exception {
        // 指定消费组名为my-consumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("my-consumer");
        // 配置namesrv地址
        consumer.setNamesrvAddr("124.57.180.156:9876");
        // 订阅topic:myTopic001 下的全部消息(因为是*,*指定的是tag标签,代表全部消息,不进行任何过滤)
        consumer.subscribe("myTopic001", "*");
        // 注册监听器,进行消息消息。
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt msg : msgs) {
                    String str = new String(msg.getBody());
                    // 输出消息内容
                    System.out.println(str);
                }
                // 默认情况下,这条消息只会被一个consumer消费,这叫点对点消费模式。也就是集群模式。
                // ack确认
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // 启动消费者
        consumer.start();
        System.out.println("Consumer start");
    }
}

2.2 、 DIFUSIÓN

Modo de transmisión.

Por ejemplo, después de iniciar cinco Consumidores y el Productor produce un mensaje, el Broker transmitirá el mensaje a los cinco Consumidores. Los cinco Consumidores consumen una vez y cada uno consume una vez.

// 代码里只需要添加如下这句话即可:
consumer.setMessageModel(MessageModel.BROADCASTING); 

2.3. Comparación de los dos modos

  • El clúster es el predeterminado de forma predeterminada y el modo de transmisión debe configurarse manualmente.
  • Un mensaje: varios consumidores en el modo de clúster solo tendrán un consumidor para consumir. Cada consumidor en el modo de transmisión consumirá este mensaje.
  • En el modo de transmisión, después de enviar un mensaje, será consumido por todos los Consumidores que se están transmitiendo actualmente, pero los Consumidores recién agregados no consumirán este mensaje. Es fácil de entender: El altavoz en la aldea gritó a toda la aldea que recolectara huevos. , y el segundo Una nueva persona en su aldea, esa persona debe haber escuchado ayer la noticia gritada por el altavoz.

3 、 TAG && CLAVE

Al enviar / consumir mensajes, puede especificar una etiqueta / clave para filtrar mensajes y admitir comodines. * Significa consumir todos los mensajes de este tema sin filtrar.

Si observa el org.apache.rocketmq.common.message.Messagecódigo fuente, puede encontrar que puede especificar etiquetas y claves al enviar mensajes:

public Message(String topic, String tags, String keys, byte[] body) {
    this(topic, tags, keys, 0, body, true);
}

tal como:

public class ProducerTagsKeys {
    public static void main(String[] args) throws Exception {
        // 指定生产组名为my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 启动Producer
        producer.start();
        // 创建消息对象,topic为:myTopic001,消息内容为:hello world,且tags为:test-tags,keys为test-keys
        Message msg = new Message("myTopic001", "test-tags", "test-keys", "hello world".getBytes());
        // 发送消息到mq,同步的
        SendResult result = producer.send(msg);
        System.out.println("发送消息成功!result is : " + result);
        // 关闭Producer
        producer.shutdown();
        System.out.println("生产者 shutdown!");
    }
}

Resultado de salida:

发送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854149DC18B4AAC26FA4B7200000, offsetMsgId=7B39B49D00002A9F0000000000058DA6, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=3], queueOffset=3]
生产者 shutdown!

Mirando la consola de control, puede encontrar que las etiquetas y claves ya están en efecto:
Inserte la descripción de la imagen aquí
si especifica * al consumir, serán todos los mensajes bajo este tema. Podemos especificar prefijos comodines, como:

// 这样就只会消费myTopic001下的tag为test-*开头的消息。
consumer.subscribe("myTopic001", "test-*");

// 代表订阅Topic为myTopic001下的tag为TagA或TagB的所有消息
consumer.subscribe("myTopic001", "TagA||TagB");

También admite el filtrado de expresiones SQL, que no es muy común. Ya no BB.

Dos, errores comunes

1 、 tiempo de espera de llamada sendDefaultImpl

1,1, anormal

Exception in thread "main" org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendDefaultImpl call timeout
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:666)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1342)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1288)
	at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:324)
	at com.chentongwei.mq.rocketmq.Producer.main(Producer.java:18)

1.2, resolver

1. Si es un servidor en la nube, primero verifique si el grupo de seguridad permite el acceso al puerto 9876, si el firewall está encendido y, si está encendido, si 9876 está asignado.

2. Modifique el archivo de configuración broker.conf, agregue:

brokerIP1=我用的是阿里云服务器,这里是我的公网IP

Agregue la IP local al iniciar namesrv y broker (yo uso el servidor Alibaba Cloud, aquí está mi IP pública):

./bin/mqnamesrv -n IP:9876
./bin/mqbroker -n IP:9876 -c conf/broker.conf

2 、 No hay información de ruta de este tema

2,1, anormal

Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: No route info of this topic: myTopic001
See http://rocketmq.apache.org/docs/faq/ for further details.
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:684)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1342)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1288)
	at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:324)
	at com.chentongwei.mq.rocketmq.Producer.main(Producer.java:18)

2.2. Resolver

Obviamente el envío fue exitoso, ya no es el tiempo de espera justo ahora, pero nos dice que no existe tal tema. Eso no se puede crear manualmente cada vez, por lo que cuando inicia el corredor, puede especificar los parámetros para permitir que el corredor lo cree automáticamente para nosotros. como sigue

./bin/mqbroker -n IP:9876 -c conf/broker.conf autoCreateTopicEnable=true
  •  

Supongo que te gusta

Origin blog.csdn.net/qq_33762302/article/details/114859240
Recomendado
Clasificación