RabbitMQ message mode 01

Message 100% delivery

 

How to protect the message 100% delivery success?

What is the reliability of the delivery end of the production?

u   ensure the success of the message sent

u   protection MQ successfully receiving node

u   sender receives MQ node ( Broker ) acknowledgment

u   sound messages compensation mechanism

 

BAT / TMD Internet giant's solution:

u   messages off the library, the message status marking

 

u   delay the delivery of the message, do secondary confirmation, check the callback  

 

 

 

 

 

Idempotence concept

What sex is power and so on?

u   we can learn optimistic locking mechanism of the database

u   For example, we perform an updated inventory of SQL statements

u  Update t_repository set count = count -1,version = version + 1 where version = 1

U   elasticsearch also strictly follow the concept of power and so on, each data update, Version + 1 (in front of bloggers blog mentioned)

Consumer end - idempotency guarantee

In the peak of the business generated by the massive orders, how to avoid duplication of spending problem message?

Consumer realize idempotency, it means that our message will never consume many times, even though we received a number of different messages

Industry mainstream idempotent operations

Unique ID + fingerprint code mechanism for using the database master keys to weight

Using Redis atomic to achieve 

The only ID + fingerprint code mechanism

Unique ID + fingerprint code mechanism for using the database master keys to weight

Select count (1) from T_order where ID = unique ID + fingerprint code

Benefits: simple

Disadvantages: There are performance bottlenecks database write under high concurrency

Solution: ID algorithmic routing sub-library sub-table

Using Redis atomic to achieve

Use Redis be idempotent, issues to be considered

First: Do we want to drop the data library, if the library is off, then the key to solving the problem is how to do the database and cache atomicity?

Second: If not for falling, so are stored in the cache, how to set the timing synchronization strategy?

 

Confirm the confirmation message

Understanding Confirm message acknowledgment.

Confirmation message refers to the producer after the delivery of the message, if Broker message is received, the producer will give us a response.        

 

The producer receives the response, to determine whether the message is transmitted normally to the Broker reliability of delivery, the message in this way is to protect the core

 

 

 

 How to achieve Confirm confirmation message?

第一步:在Channel上开启确认模式:channel.confirmSelect()

第二步:在channel上添加监听:addConfirmListener,监听成功和失败的返回结果,根据具体的结果对消息进行重新发送、或记录日志等后续处理!

消费端代码

package com.cjh.rabbitmqapi.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @author
 * @site
 * @company
 * @create  2019-11-20 9:12
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        //1 创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2 获取C    onnection
        Connection connection = connectionFactory.newConnection();

        //3 通过Connection创建一个新的Channel
        Channel channel = connection.createChannel();

        String exchangeName = "test_confirm_exchange";
        String routingKey = "confirm.#";
        String queueName = "test_confirm_queue";

        //4 声明交换机和队列 然后进行绑定设置, 最后制定路由Key
        channel.exchangeDeclare(exchangeName, "topic", true);
        channel.queueDeclare(queueName, true, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        //5 创建消费者
        QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
        channel.basicConsume(queueName, true, queueingConsumer);

        while(true){
            QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
            String msg = new String(delivery.getBody());

            System.err.println("消费端: " + msg);
        }


    }
}

服务提供方代码

package com.cjh.rabbitmqapi.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;

/**
 * @author
 * @site
 * @company
 * @create  2019-11-20 9:12
 */
public class Producer {
    public static void main(String[] args) throws Exception {
        //1 创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        //2 获取C    onnection
        Connection connection = connectionFactory.newConnection();

        //3 通过Connection创建一个新的Channel
        Channel channel = connection.createChannel();


        //4 指定我们的消息投递模式: 消息的确认模式
        channel.confirmSelect();

        String exchangeName = "test_confirm_exchange";
        String routingKey = "confirm.save";

        //5 发送一条消息
        String msg = "Hello RabbitMQ Send confirm message!";
        channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());

        //6 添加一个确认监听
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.err.println("-------no ack!-----------");
            }

            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.err.println("-------ack!-----------");
            }
        });
    }
}

 

 

Return返回消息

Return Listener用于处理一些不可路由的消息!

正常情况:我们的消息生产者,通过指定一个ExchangeRoutingKey,把消息送达到某一个队列中去,然后我们的消费者监听队列,进行消费处理操作!

异常情况:在某些情况下,如果我们在发送消息的时候,当前的Exchange不存在或者指定的路由key路由不到,这个时候如果我们需要监听这种不可达的消息,就需要使用Return Listener

 

在基础API中有一个关键的配置项

 

Mandatory:如果为true,则监听器会接收到路由不可达的消息,然后进行后续处理,如果为false,那么Broker端自动删除该消息!

 

 

 

 

 消费端代码

package com.cjh.rabbitmqapi.returnlistener;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2019-11-20 9:58
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchangeName = "test_return_exchange";
        String routingKey = "return.#";
        String queueName = "test_return_queue";

        channel.exchangeDeclare(exchangeName, "topic", true, false, null);
        channel.queueDeclare(queueName, true, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        QueueingConsumer queueingConsumer = new QueueingConsumer(channel);

        channel.basicConsume(queueName, true, queueingConsumer);

        while(true){
            QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.err.println("消费者: " + msg);
        }

    }
}

生产端代码

package com.cjh.rabbitmqapi.returnlistener;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author
 * @site
 * @company
 * @create  2019-11-20 9:44
 */
public class Producer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchange = "test_return_exchange";
        String routingKey = "return.save";
        String routingKeyError = "abc.save";

        String msg = "Hello RabbitMQ Return Message";


        channel.addReturnListener(new ReturnListener() {
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange,
                                     String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {

                System.err.println("---------handle  return----------");
                System.err.println("replyCode: " + replyCode);
                System.err.println("replyText: " + replyText);
                System.err.println("exchange: " + exchange);
                System.err.println("routingKey: " + routingKey);
                System.err.println("properties: " + properties);
                System.err.println("body: " + new String(body));
            }
        });

        //消息投递成功,会被消费者所消费
        channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
        //消息不可达,将触发ReturnListener
//         channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
    }
}

 

 

 

 

自定义消费者

我们一般就是在代码中编写while循环,进行consumer.nextDelivery方法进行获取下一条消息,然后进行消费处理!

但是我们使用自定义的Consumer更加的方便,解耦性更加的强,也是实际工作中最常用的使用方式!

 

自定义消费端代码

package com.cjh.rabbitmqapi.consumer;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author 
 * @site 
 * @company
 * @create  2019-11-20 10:13
 */
public class MyConsumer extends DefaultConsumer {


    public MyConsumer(Channel channel) {
        super(channel);
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.err.println("-----------consume message----------");
        System.err.println("consumerTag: " + consumerTag);
        System.err.println("envelope: " + envelope);
        System.err.println("properties: " + properties);
        System.err.println("body: " + new String(body));
    }
}

消费端调用

package com.cjh.rabbitmqapi.consumer;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2019-11-20 10:14
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();


        String exchangeName = "test_consumer_exchange";
        String routingKey = "consumer.#";
        String queueName = "test_consumer_queue";

        channel.exchangeDeclare(exchangeName, "topic", true, false, null);
        channel.queueDeclare(queueName, true, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        channel.basicConsume(queueName, true, new MyConsumer(channel));


    }
}

生产端调用

package com.cjh.rabbitmqapi.consumer;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2019-11-20 10:12
 */
public class Producer {
    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.198.147");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        String exchange = "test_consumer_exchange";
        String routingKey = "consumer.save";

        String msg = "Hello RabbitMQ Consumer Message";

        for(int i =0; i<5; i ++){
            channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
        }

    }
}

 

 

 

 

 

Guess you like

Origin www.cnblogs.com/chenjiahao9527/p/11994329.html
Recommended