6-2 Integración de casos simple de conceptos básicos de cola de mensajes

Directorio de artículos

1 Concepto básico de cola de mensajes

producerGroup:生产者组
producer:生产者
consumerGroup:消费者组
consumer:消费者
nameServer(邮局):协调者,broker注册信息,发送者和接收者通过它获取broker信息
broker(邮递员):负责消息的接收、存储、显示
topic(地区):消息主题类型,发送接收都需要创建topic
messageQueue(邮件):消息队列,一个topic可以有多个messageQueue
message(邮件内容):具体消息

2 La clase de prueba implementa el envío y la recepción de mensajes

Demostración simple en orden de servicio

2.1 Agregar dependencia de jar

        <!--添加rocketmq的依赖-->
        <dependency>
            <groupId>org.apache.rockemq</groupId>
            <artifactId>roketmq-spring-boot-starter</artifactId>
            <version>2.0.2</version>
        </dependency>

2.2 Cree una clase de envío de mensajes RocketMQSendTest en / src / test / java y verifique el resultado del mensaje de la consola después del inicio

package cn.hzp;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

/**
 * - 1 创建消息生产者DefaultMQProducer,设置组名produceGroup
 * - 2 为生产者指定nameserver地址192.168.149.11:9876
 * - 3 启动生产者start
 * - 4 创建消息对像Message:主题myTopic、标签myTag、消息体String.getBytes();
 * - 5 发送消息send
 * - 6 关闭生产者shutdown
 */
public class RocketMQSendTest {
    public static void main(String[] args) {
        try {
            DefaultMQProducer produceGroup = new DefaultMQProducer("produceGroup");
            produceGroup.setNamesrvAddr("192.168.149.11:9876");
            produceGroup.start();
            Message message = new Message("myTopic", "myTag", "消息体".getBytes());
            SendResult sendResult = produceGroup.send(message, 10000);
            System.out.println(sendResult);
            produceGroup.shutdown();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

2.3 Cree la clase de recepción de mensajes RocketMQReceiveTest en / src / test / java, comience a ver el resultado y luego inicie la clase de envío para ver el resultado

package cn.hzp;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;

import java.util.List;

/**
 * - 1 创建消费者DefaultMQPushConsumer,组名consumerGroup
 * - 2 指定nameserver地址192.168.149.11:9876
 * - 3 指定消费者订阅subscribe的主题myTopic和标签*
 * - 4 设置回调函数registerMessageListener,通过MessageListenerConcurrently对象编写处理消息方法,返回消息状态ConsumeConcurrentlyStatus.CONSUME_SUCCESS
 * - 5 启动消费者start
 */
public class RocketMQReceiveTest {
    public static void main(String[] args) {
        try {
            DefaultMQPushConsumer consumerGroup = new DefaultMQPushConsumer("consumerGroup");
            consumerGroup.setNamesrvAddr("192.168.149.11:9876");
            consumerGroup.subscribe("myTopic", "*");
            consumerGroup.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
                System.out.println("消息列表为:" + list);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            });
            consumerGroup.start();
            System.out.println("消费者启动");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

3 Simulación empresarial simple

El servicio de pedidos envía un mensaje cuando el pedido se realiza correctamente, y el servicio de productos escucha el mensaje y envía una notificación por SMS

3.1 orden para generar el código final del mensaje

3.1.1 El archivo pom del servicio de pedidos presenta 2 dependencias mq

rocketmq-spring-boot-starter2.0.2 和 rocketmq-client4.4.0

        <!--添加rocketmq的依赖-->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.4.0</version>
        </dependency>

3.1.2 Agregar configuración al archivo yml del servicio de pedidos

rocketmq:
  # rocketmq服务地址
  name-server: 192.168.149.11:9876
  # 生产者组
  producer:
    group: shop-order

3.1.3 Enviar mensaje mq en el método de pedidorocketMQTemplate.convertAndSend("orderTopic", order);

3.1.4 Después de iniciar el servicio y acceder al navegador, http://localhost:8091/order/product/1verifique el mensaje con el orden de temas Tema en la consola mq

3.2 código final del mensaje de consumo del productor

3.2.1 El archivo pom del servicio del producto introduce 2 dependencias mq, lo mismo que 3.1.1

3.2.2 Agregar configuración del archivo yml del servicio del producto

rocketmq:
  # rocketmq服务地址
  name-server: 192.168.149.11:9876

3.2.3 El servicio del producto agrega la clase receptora cn.hzp.mq.SmsService

package cn.hzp.mq;

import cn.hzp.domain.Order;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * - 需要实现RocketMQListener<消息内容>,消息内容为Order
 * 	- 监听注解@RocketMQMessageListener,
 * 		- 指定消费者组名consumerGroup
 * 		- 消费主题topic
 * 		- consumeMode消费模式,ORDERLY顺序,CONCURRENTLY默认同步即没顺序
 * 		- messageModel消息模式,
 * 			广播BRODCASTING,一个消息被多个消费者多次消费
 * 			默认集群CLUSTERING,一个消息只能被一个消费者消费
 */
@Slf4j
@Service
@RocketMQMessageListener(
        consumerGroup = "productGroup",
        topic = "orderTopic",
        consumeMode = ConsumeMode.CONCURRENTLY,
        messageModel = MessageModel.CLUSTERING
)
public class SmsService implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        log.info("接收消息:{},接下来发送短信", order);
    }
}

3.2.4 Reiniciar el servicio del producto, solicitud del navegador http://localhost:8091/order/product/1, ver la salida de información de la idea

Demostración de tipo de mensaje 4 mq

Mensaje común (asíncrono, síncrono, unidireccional), mensaje secuencial, mensaje de transacción

4.1 Probar mensajes comunes

Incluyendo sincronización confiable, transmisión asíncrona confiable, unidireccional

  • Sincronización confiable: el remitente espera a que el receptor envíe el segundo mensaje después de recibir la notificación de confirmación del mensaje, como la notificación por SMS de registro.
  • Confiable y asincrónico: el remitente simplemente envía el mensaje y procesa el resultado de la respuesta del receptor de forma asincrónica a través de la interfaz de devolución de llamada, que se utiliza para un enlace largo, como la transcodificación del consumidor después de la carga del video
  • Envío unidireccional: solo enviar, como recopilación de registros

4.1.1 El archivo pom del servicio de pedidos introduce dependencias de prueba

        <!--测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

4.1.2 En src / test / java, cree una nueva clase de prueba cn.hzp.RocketMQMessageTypeTest

package cn.hzp;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = OrderApplication.class)
@Slf4j
public class RocketMQMessageTypeTest {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Test
    public void ordinaryMessage() throws Exception {
        // 同步消息,第一个参数为topic:tag,tag可以为空,直接写topic;
        SendResult sendResult = rocketMQTemplate.syncSend("typeTopicSync:tag1", "这是同步消息", 10000);
        log.info("同步消息发送结果为:{}", sendResult);

        // 异步消息
        rocketMQTemplate.asyncSend("typeTopicAync", "这是异步消息", new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("消息发送成功,{}", sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                log.info("消息发送异常,{}", throwable.getMessage());
            }
        });
        log.info("异步消息准备要发送了");
        Thread.sleep(30000);

        // 单向消息
        rocketMQTemplate.sendOneWay("typeTopicOneWay", "这是单向消息");
    }
}

4.1.3 Inicie la prueba y vea la información del mensaje de la consola web mq

4.2 Secuencia de mensajes, agregue métodos de prueba a la clase RocketMQMessageTypeTest

Se enviarán varios mensajes del mismo tema a messageQueue y el método de envío se puede agregar con el sufijo ordenado, como un mensaje secuencial unidireccional.

    @Test
    public void orderMessage() {
        // 单向顺序消息,hashkey为分配到哪个队列的key
        for (int i = 0; i < 10; i++) {
            rocketMQTemplate.sendOneWayOrderly("typeTopicOneWay", "这是单向消息", "xxx");
        }
    }

Prueba, comprueba la información de posición máxima de la cola en el estado en la pestaña de tema de la consola web mq

4.3 Mensaje de transacción

Proceso de implementación:

  • 1 El remitente (servicio de pedido) envía un mensaje semitransacional (pedido de información del pedido) al servidor mq, y el servidor mq responde que el mensaje semitransacional se envió correctamente
  • 2 El remitente (servicio de orden de pedido) ejecuta una transacción local (operación de orden), el resultado de la ejecución le dice al servidor mq, el servidor mq confirma o lanza el mensaje de acuerdo con el resultado
  • 3 Si el servidor mq no recibe el resultado de la ejecución, comprobará el estado de la transacción local y mq enviará el mensaje después de recibir la confirmación.

4.3.1 El servicio de pedidos envía un mensaje semi-transaccional, agregue el método de prueba en la clase OrderController

    /**
     * 测试mq的事务消息
     * - 1 向mq服务端发送半事务消息,mq服务端回应消息接收情况
     * - 2 执行本地事务下单操作,结果通知mq服务端,成功commit失败rollback
     * - 3 mq服务端没有收到通知,回查本地事务,成功commit失败rollback
     */
    @RequestMapping("/order/testMQ")
    public TransactionSendResult testRocketMQ(){
        Order order = Order.builder()
                .pid(1)
                .uid(1).uname("测试用户")
                .number(1)
                .build();
        // 演示,半事务消息,这里发送半事务消息,可以放在单独service操作
        UUID uuid = UUID.randomUUID();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "txProducerGroup",
                "topicTransaction:tagOrder",
                MessageBuilder.withPayload(order).setHeader("txId", uuid).build(),
                order);
        log.info("发送事务消息,结果为{}", transactionSendResult);
        return transactionSendResult;
    }

4.3.2 El servicio de dominio agrega el registro de transacciones local txLog, que es conveniente para que el servidor mq revise los resultados de ejecución de la transacción de operación de orden local.

@Data
@Entity(name = "shop_txlog")
public class TxLog {
    @Id
    private String txId;
    private Date date;
}

4.3.3 El servicio de pedidos agrega la capa de persistencia del registro de transacciones cn.hzp.dao.TxLogDao en el directorio src / main / java

public interface TxLogDao extends JpaRepository<TxLog, String> {
}

4.3.4 In orderService e impl se agregan para guardar los registros de transacciones al realizar pedidos, lo cual es conveniente para que el servidor mq vuelva a verificar

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveMQTest(String txId, Order order) {
        TxLog txLog = new TxLog();
        txLog.setTxId(txId);
        txLog.setDate(new Date());
        TxLog txLogResult = txLogDao.save(txLog);
        log.info("保存日志信息,便于mq回查事务结果,{}", txLogResult);
        Order orderResult = orderDao.save(order);
        log.info("保存订单信息,{}", orderResult);
    }

4.3.5 Cree una nueva clase cn.hzp.mq.MQTranListener en el directorio src / main / java para implementar transacciones locales (pedidos) y revisión de mensajes

package cn.hzp.mq;

import cn.hzp.dao.TxLogDao;
import cn.hzp.domain.Order;
import cn.hzp.domain.TxLog;
import cn.hzp.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = "txProducerGroup")
public class MQTranListener implements RocketMQLocalTransactionListener {
    @Autowired
    private OrderService orderService;
    @Autowired
    private TxLogDao txLogDao;

    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) {
        try {
            log.info("执行本地事务:进行下单操作");
            String txId = (String) message.getHeaders().get("txId");
            Order order = (Order) arg;
            orderService.saveMQTest(txId, order);
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
        log.info("mq服务端执行消息回查");
        String txId = (String) message.getHeaders().get("txId");
        TxLog txLog = txLogDao.findById(txId).get();
        if (txLog != null) {
            log.info("回查结果,下单成功");
            return RocketMQLocalTransactionState.COMMIT;
        } else {
            log.info("回查结果,下单失败");
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }
}

4.3.6 Iniciar el servicio de pedidos,

  • Solicitud del navegador para http://localhost:8091/order/testMQprobar la transacción local
  • Interrumpa el punto de interrupción antes del retorno del método executeLocalTransaction, luego detenga el servicio y reinicie, y vuelva a probar y verifique

Supongo que te gusta

Origin blog.csdn.net/weixin_45544465/article/details/106003654
Recomendado
Clasificación