RabbitMQ (6) habla sobre el exitoso mecanismo de confirmación de RabbitMQ

RabbitMQ (6) habla sobre el exitoso mecanismo de confirmación de RabbitMQ

En escenarios reales, se debe garantizar que los mensajes enviados por algunos productores se envíen con éxito a la cola de mensajes, entonces, ¿cómo asegurar una entrega exitosa?

  • Mecanismo de transacción
  • Mecanismo de confirmación de lanzamiento

Mecanismo de transacción

  • El protocolo AMQP proporciona una forma de garantizar la entrega exitosa de mensajes, habilitando el modo transaccional a través del canal

  • Y utilice los tres métodos del canal para enviar el mensaje en modo transacción, si el envío falla, la transacción se revierte mediante el manejo de excepciones para garantizar que el mensaje se entregue correctamente.

    • channel.txSelect (): transacción abierta
    • channel.txCommit (): confirma la transacción
    • channel.txRollback (): deshacer la transacción
  • Spring ha encapsulado los tres métodos anteriores, por lo que solo puede jugar con el código original

Productor

public class Sender {
    
    

    public static void main(String[] args) {
    
    

        //获得连接
        Connection connection = ConnectionUtil.getConnection();
        try {
    
    
            //在连接中创建通道
            Channel channel = connection.createChannel();
            //声明路由
            /**
             * String var1: 路由名
             * String var2: 路由类型
             *      topic : 模糊匹配的定向分发
             */
            channel.exchangeDeclare("test_transaction","topic");
            //开启事务
            channel.txSelect();
            try {
    
    
                /**
                 * 发送消息有四个参数
                 * String var1: (路由)交换机名称,当前是简单模式,也就是P2P模式,无交换机,所以名称为""
                 * String var2: 目标队列名称
                 * BasicProperties var3: 设置消息的属性(没有属性则为null)
                 * byte[] var4: 消息内容(只接受字节数组)
                 */
                channel.basicPublish("test_transaction", "product.price", null, "商品 1 降价".getBytes());
                System.out.println(1 / 0);
                channel.basicPublish("test_transaction", "product.price", null, "商品 2 降价".getBytes());
                //提交事务
                channel.txCommit();
                System.out.println("生产者 : 消息已发送!");
            } catch (IOException e) {
    
    
                System.out.println("消息全部撤销!");
                e.printStackTrace();
                //回滚事务
                channel.txRollback();
            } finally {
    
    
                channel.close();
                //释放资源
                ConnectionUtil.close(connection);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

    }

}

consumidor

public class Recer {
    
    

    public static void main(String[] args) {
    
    
        //获得连接
        Connection connection = ConnectionUtil.getConnection();
        try {
    
    
            //获得通道(信道)
            Channel channel = connection.createChannel();
            //声明消息队列
            //queueDeclare : 此方法有双重作用,如果队列不存在则创建队列,若队列存在则获取
            channel.queueDeclare("test_transaction_queue", false, false, false, null);
            //绑定路由(关注)
            /**
             * String var1: 队列名
             * String var2: 路由名
             * String var3:
             *      匹配用户开头的所有操作
             */
            channel.queueBind("test_transaction_queue","test_transaction","product.#");
            //从信道中获得消息
            DefaultConsumer consumer = new DefaultConsumer(channel){
    
    
                /**
                 * 交付处理,重写方法
                 * @param consumerTag   收件人信息
                 * @param envelope      信封,包裹上的快递标签
                 * @param properties    协议配置
                 * @param body          消息
                 * @throws IOException
                 */
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                    //将消息字节构建成字符串
                    //boby就是在队列中获取的消息
                    String string = new String(body);
                    System.out.println("消费者 1 : " + string);
                }
            };
            //监听队列
            /**
             * String var1: 队列名称
             * boolean var2: 自动消息确认
             * Consumer var3: 从管道中获取信息
             */
            channel.basicConsume("test_transaction_queue",true,consumer);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

}

Confirmar mecanismo de confirmación de lanzamiento

  • Para garantizar la entrega exitosa de mensajes, RabbitMQ adopta un esquema que nos proporciona un mecanismo de transacción a través del nivel de protocolo AMQP, pero el uso de transacciones reducirá en gran medida el rendimiento de los mensajes.
  • El resultado de la prueba de mi disco duro SSD local mostró que los mensajes de 10w no iniciaron la transacción, y la transacción se completó en aproximadamente 8 segundos. Después de que se inició la transacción, tomó casi 310 segundos, que fue más de 30 veces peor.
Using standard AMQP 0-9-1, the only way to guarantee that a message isn’t lost is by using transactions – make the channel transactional then for each message or set of messages publish, commit. In this case, transactions are nnecessarily heavyweight and decrease throughput by a factor of 250. To remedy this, a confirmation mechanism was introduced. It mimics the consumer acknowledgements mechanism already present in the protocol.

关键性译文:开启事务性能最大损失超过250倍
  • Entonces, ¿existe una solución más eficiente? La respuesta es adoptar el modo Confirmar.
  • ¿Por qué la eficiencia de las transacciones es tan baja? Imagínese: 10 mensajes, los primeros 9 tuvieron éxito, si el décimo falla, entonces los 9 mensajes deben cancelarse y revertirse. Desperdicio de esposa
  • El modo de confirmación utiliza las medidas de volver a emitir el artículo 10 para completar la entrega de 10 mensajes.

Aplicación en primavera

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/rabbit
        https://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--配置RabbitMQ连接-->
    <!--publisher-confirms="true" : 开启生产者确认机制-->
    <rabbit:connection-factory id="connectionFactory" host="106.75.245.83" port="5672" username="admin" password="admin"
                               virtual-host="/szx" publisher-confirms="true"></rabbit:connection-factory>

    <!--配置队列-->
    <rabbit:queue name="test_spring_queue_1"></rabbit:queue>

    <!--配置RabbitMQAdmin,主要用于在Java代码中对队列的管理,用来创建,绑定,删除队列与交换机,发送消息等操作-->
    <rabbit:admin connection-factory="connectionFactory"></rabbit:admin>

    <!--配置路由,topic类型-->
    <rabbit:topic-exchange name="spring_topic_exchange">
        <!--绑定队列-->
        <rabbit:bindings>
            <rabbit:binding pattern="msg.#" queue="test_spring_queue_1"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--配置json转换工具,将消息转换为json-->
    <bean id="jsonMessageConverter"
          class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"></bean>

    <!--配置RabbitMQ的模板-->
    <!--confirm-callback="messsageConfirm" : 添加确认回调处理类-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="spring_topic_exchange"
                     message-converter="jsonMessageConverter" confirm-callback="messsageConfirm"></rabbit:template>

    <!--配置确认机制的处理类-->
    <bean id="messsageConfirm" class="com.szx.confirm.MesssageConfirm"></bean>

</beans>
public class MesssageConfirm implements RabbitTemplate.ConfirmCallback {
    
    

    /**
     * @param correlationData 消息相关的数据对象(封装了消息的唯一 ID )
     * @param b 消息是否确认成功
     * @param s 异常信息
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
    
    
        if (b){
    
    
            System.out.println("消息确认成功!");
        } else {
    
    
            System.out.println("消息确认失败!");
            System.out.println(s);
            // 如果本条消息一定要发送到队列中,例如下订单消息,我们可以采用消息补发
            // 采用递归(固定次数,不可无限)或 redis+定时任务
        }
    }

}
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=rabbitmq.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l%m%n
log4j.rootLogger=debug, stdout,file
public class Sender {
    
    

    public static void main(String[] args) {
    
    
        //获取spring的配置文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-rabbitmq-producer.xml");
        //与容器中获取RabbitMQ模板类
        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
        //发送消息
        Map<String,String> map = new HashMap<String, String>();
        map.put("name","小星");
        map.put("email","[email protected]");
        /**
         * String routingKey: 指定路由键
         * Object object: 发送的数据
         */
        rabbitTemplate.convertAndSend("msg.user",map);
        System.out.println("消息已发送!");
        context.close();
    }

}

Supongo que te gusta

Origin blog.csdn.net/weixin_49741990/article/details/113107718
Recomendado
Clasificación