Los mensajes ordenados se entregan y consumen en un orden estricto de primero en entrar, primero en salir (FIFO). Este tema proporciona código de ejemplo en Java para enviar y recibir mensajes ordenados mediante TCP con el SDK de Apache RocketMQ para Java.
Funcionamiento de los mensajes ordenados
Los mensajes ordenados se dividen en particiones mediante una sharding key. Cada sharding key se asigna a una partición dedicada, y los mensajes dentro de esa partición se entregan en el mismo orden en que se enviaron.
Tres reglas rigen este comportamiento:
Orden dentro de una partición: los mensajes que comparten la misma sharding key siempre se entregan en orden FIFO.
Sin orden entre particiones: los mensajes con distintas sharding keys pueden llegar en cualquier orden relativo entre sí.
La sharding key es obligatoria: todo mensaje ordenado debe incluir una sharding key. Esta clave es independiente de la clave general del mensaje que se usa para búsquedas.
Utilice las sharding keys para modelar los requisitos de orden de su negocio. Por ejemplo, establezca la sharding key como un ID de pedido para que todos los mensajes del mismo pedido se procesen de forma secuencial, mientras que los mensajes de distintos pedidos se procesen en paralelo.
Para obtener más información sobre la semántica y las restricciones de ordenamiento, consulte Ordered messages.
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
El SDK para Java 1.2.7 o posterior. Consulte Release notes
Un entorno preparado. Consulte Prepare the environment
(Opcional) Configuración de registros. Consulte Logging settings
Si es nuevo en ApsaraMQ for RocketMQ, comience con el demo project para configurar un proyecto funcional antes de enviar y recibir mensajes.
Envío de mensajes ordenados
Garantía de orden
El broker determina el orden de los mensajes según la secuencia en la que el remitente utiliza un único producer o hilo para enviarlos. Si varios producers o hilos envían mensajes de forma concurrente, el broker los ordena por hora de llegada, lo cual puede diferir del orden de negocio previsto.
Código de ejemplo
Reemplace los siguientes marcadores de posición con sus valores reales:
|
Marcador de posición |
Descripción |
Ejemplo |
|
|
Group ID creado en la consola de ApsaraMQ for RocketMQ |
GID_order_group |
|
|
AccessKey ID para autenticación |
LTAI5tXxx |
|
|
AccessKey secret para autenticación |
xXxXxXx |
|
|
Endpoint TCP de la sección TCP Endpoint en la página Instance Details |
http://MQ_INST_xxx.mq-internet-access.mq-internet.aliyuncs.com:80 |
Para ver el código completo, consulte el repositorio de código de ApsaraMQ for RocketMQ.
package com.aliyun.openservices.ons.example.order;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.order.OrderProducer;
import java.util.Date;
import java.util.Properties;
public class ProducerClient {
public static void main(String[] args) {
Properties properties = new Properties();
// Group ID created in the ApsaraMQ for RocketMQ console.
properties.put(PropertyKeyConst.GROUP_ID, "<your-group-id>");
// AccessKey ID for authentication.
properties.put(PropertyKeyConst.AccessKey, "<your-access-key>");
// AccessKey secret for authentication.
properties.put(PropertyKeyConst.SecretKey, "<your-secret-key>");
// TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
properties.put(PropertyKeyConst.NAMESRV_ADDR, "<your-tcp-endpoint>");
OrderProducer producer = ONSFactory.createOrderProducer(properties);
// Call start() once before sending any messages.
producer.start();
for (int i = 0; i < 1000; i++) {
String orderId = "biz_" + i % 10;
Message msg = new Message(
// Topic for ordered messages.
"Order_global_topic",
// Tag for filtering on the broker side.
"TagA",
// Message body in binary format.
// Producer and consumer must agree on serialization and deserialization.
"send order global msg".getBytes()
);
// Message key -- a business identifier, ideally globally unique.
// Use this key to look up the message in the ApsaraMQ for RocketMQ console.
msg.setKey(orderId);
// Sharding key -- determines the partition for ordered delivery.
// Messages with the same sharding key are delivered in FIFO order.
String shardingKey = String.valueOf(orderId);
try {
SendResult sendResult = producer.send(msg, shardingKey);
if (sendResult != null) {
System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());
}
} catch (Exception e) {
// Handle failure: retry or persist the message for later resend.
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
}
// Shut down the producer before exiting.
// Skip this if the producer sends messages frequently.
producer.shutdown();
}
}
Recepción de mensajes ordenados
Comportamiento de reintentos
Cuando falla el consumo de un mensaje ordenado, el broker bloquea todos los mensajes subsiguientes en la misma partición hasta que el mensaje actual se procese correctamente o alcance el número máximo de reintentos. Este comportamiento de bloqueo preserva el orden FIFO, pero puede retrasar el procesamiento de otros mensajes en la partición.
Dos parámetros controlan el comportamiento de los reintentos:
|
Parámetro |
Descripción |
Valores válidos |
|
|
Tiempo de espera en milisegundos entre reintentos para un mensaje ordenado fallido |
10 -- 30,000 |
|
|
Número máximo de intentos de reintento antes de omitir el mensaje |
Entero (por ejemplo, |
Establezca estos valores según su tolerancia a los retrasos en el procesamiento. Un valor menor de SuspendTimeMillis permite reintentar más rápido, pero incrementa la carga del broker. Un valor menor de MaxReconsumeTimes desbloquea la partición antes, pero podría omitir mensajes que habrían tenido éxito con más reintentos.
Código de ejemplo
El siguiente código de ejemplo utiliza los mismos marcadores de posición que el ejemplo del producer. Reemplácelos con sus valores reales antes de ejecutar el código.
package com.aliyun.openservices.ons.example.order;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.order.ConsumeOrderContext;
import com.aliyun.openservices.ons.api.order.MessageOrderListener;
import com.aliyun.openservices.ons.api.order.OrderAction;
import com.aliyun.openservices.ons.api.order.OrderConsumer;
import java.util.Properties;
public class ConsumerClient {
public static void main(String[] args) {
Properties properties = new Properties();
// Consumer group ID created in the ApsaraMQ for RocketMQ console.
properties.put(PropertyKeyConst.GROUP_ID, "<your-group-id>");
// AccessKey ID for authentication.
properties.put(PropertyKeyConst.AccessKey, "<your-access-key>");
// AccessKey secret for authentication.
properties.put(PropertyKeyConst.SecretKey, "<your-secret-key>");
// TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
properties.put(PropertyKeyConst.NAMESRV_ADDR, "<your-tcp-endpoint>");
// Wait time (ms) before retrying a failed ordered message. Valid values: 10 to 30,000.
properties.put(PropertyKeyConst.SuspendTimeMillis, "100");
// Maximum retry attempts for a failed message.
properties.put(PropertyKeyConst.MaxReconsumeTimes, "20");
OrderConsumer consumer = ONSFactory.createOrderedConsumer(properties);
consumer.subscribe(
// Topic to subscribe to.
"Order_global_topic",
// Tag filter expression:
// "*" -- subscribe to all tags
// "TagA || TagB || TagC" -- subscribe to TagA, TagB, or TagC only
"*",
new MessageOrderListener() {
/**
* Return OrderAction.Success after processing the message.
* Return OrderAction.Suspend if processing fails or throws
* an exception -- the broker will retry after SuspendTimeMillis.
*/
@Override
public OrderAction consume(Message message, ConsumeOrderContext context) {
System.out.println(message);
return OrderAction.Success;
}
});
// Call start() once to begin consuming.
consumer.start();
}
}
Siguientes pasos
Obtenga información sobre los conceptos y restricciones de ordered messages.
Explore el repositorio de código completo de ApsaraMQ for RocketMQ para consultar ejemplos adicionales.