RabbitMQ

RabbitMQ平台创建用户 zxj  123,创建vhost     /vhost_mmr

1、工具类

package com.zxj.utils;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/2 16:38
 */
public class ConnectionUtil {

    public static Connection getConnection() throws IOException, TimeoutException {

        //定义一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();

        //设置服务地址
        factory.setHost("127.0.0.1");

        //设置端口号
        factory.setPort(5672);

        //设置vhost
        factory.setVirtualHost("/vhost_mmr");

        //用户名
        factory.setUsername("zxj");

        //密码
        factory.setPassword("123");

        return factory.newConnection();
    }
}

2、点对点方式

2.1、生产者

package com.zxj.simple;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 简单模式 点对点  生产者
 * @author zxj
 * @date 2020/6/2 16:43
 */
public class Send {

    private static final String QUEUE_NAME="test_simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //获取一个连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取一个通道
        Channel channel = connection.createChannel();

        //创建队列声明
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        String msg = "hello simple";

        //发送消息
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

        System.out.println("====== send msg : " + msg);

        channel.close();
        connection.close();

    }
}

2.2、消费者

package com.zxj.simple;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 简单模式 点对点  消费者
 * @author zxj
 * @date 2020/6/2 16:52
 */
public class Recv {
    private static final String QUEUE_NAME="test_simple_queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

//        oldAPI();

        newAPI();
    }


    public static void newAPI() throws IOException, TimeoutException {
        //获取一个连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取一个通道
        Channel channel = connection.createChannel();

        //创建队列声明
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //定义一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            //获取到达队列的消息
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgRecv = new String(body, "utf-8");
                System.out.println("newApi msgRecv : " + msgRecv);
            }
        };

        //监听队列
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }

    public void oldAPI() throws IOException, TimeoutException, InterruptedException{
        //获取一个连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取一个通道
        Channel channel = connection.createChannel();

        //定义一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //监听消息队列
        channel.basicConsume(QUEUE_NAME, true, consumer);

        while (true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();

            String msgRecv = new String(delivery.getBody());

            System.out.println("Recv msg : " + msgRecv);
        }
    }
}

3、WorkQueue 轮询分发(默认)  一个生产者对多个消费者

3.1、生产者

package com.zxj.workqueue;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 *
 * 默认是轮询分发消息
 *
 * 生产者
 * @author zxj
 * @date 2020/6/2 17:22
 */
public class Send {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        Channel channel = connection.createChannel();

        //声明消息队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //发送消息
        for (int i = 0; i < 50; i++) {
            String msg = "hello work queue : " + i;
            channel.basicPublish("", QUEUE_NAME,null, msg.getBytes());
            TimeUnit.MILLISECONDS.sleep(i*20);
        }

        //关闭通道和连接
        channel.close();
        connection.close();
    }

}

3.2、消费者1与消费者2

package com.zxj.workqueue;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 * 消费者1
 * @author zxj
 * @date 2020/6/2 17:28
 */
public class Recv1 {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        Channel channel = connection.createChannel();

        //声明消息队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgRecv1 = new String(body, "utf-8");
                System.out.println("1    msgRecv1 : " + msgRecv1);

                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        //监听消息队列
        boolean autoAck = true;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}
package com.zxj.workqueue;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 * 消费者2
 * @author zxj
 * @date 2020/6/2 17:28
 */
public class Recv2 {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        Channel channel = connection.createChannel();

        //声明消息队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgRecv2 = new String(body, "utf-8");
                System.out.println("2    msgRecv2 : " + msgRecv2);

                try {
                    TimeUnit.MILLISECONDS.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        //监听消息队列
        boolean autoAck = true;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}

 

4、WorkQueue 公平分发, 

每个消费者一次只处理一条消息,处理完成后返回ack给消息队列,获取下一条消息, 消费者需要关闭ack的自动应答,改为手动应答

4.1生产者

package com.zxj.workqueuefair;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 *
 * 默认是轮询分发消息
 * 此处使用公平分发模式,即每个消费者一次只处理一条消息,处理完成后返回ack给消息队列,获取下一条消息,
 * 消费者需要关闭ack的自动应答,改为手动应答
 *
 * 生产者
 * @author zxj
 * @date 2020/6/2 17:22
 */
public class Send {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        Channel channel = connection.createChannel();

        //声明消息队列
        boolean durable = false;//是否开启消息持久化,false不开启,true开启
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);


        // 生产者开启公平分发
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);

        //发送消息
        for (int i = 0; i < 50; i++) {
            String msg = "hello work queue : " + i;
            channel.basicPublish("", QUEUE_NAME,null, msg.getBytes());
            TimeUnit.MILLISECONDS.sleep(i*20);
        }

        //关闭通道和连接
        channel.close();
        connection.close();
    }

}

4.2、消费者1和消费者2

package com.zxj.workqueuefair;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 * 消费者1
 * @author zxj
 * @date 2020/6/2 17:28
 */
public class Recv1 {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        final Channel channel = connection.createChannel();

        //声明消息队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //消费者开启公平模式,一次只消费一条消息
        channel.basicQos(1);

        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgRecv1 = new String(body, "utf-8");
                System.out.println("1    msgRecv1 : " + msgRecv1);

                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // ack手动回执
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;  //true 自动应答, false手动应答  channel.basicAck(。。。)
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}
package com.zxj.workqueuefair;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * work queue 工作队列 一个生产者对多个消费者
 * 消费者2
 * @author zxj
 * @date 2020/6/2 17:28
 */
public class Recv2 {

    private static final String QUEUE_NAME = "test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();

        //从连接中获取通道
        final Channel channel = connection.createChannel();

        //声明消息队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //消费者开启公平模式,一次只消费一条消息
        channel.basicQos(1);

        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgRecv2 = new String(body, "utf-8");
                System.out.println("2    msgRecv2 : " + msgRecv2);

                try {
                    TimeUnit.MILLISECONDS.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // ack手动回执
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;   //true 自动应答, false手动应答  channel.basicAck(。。。)
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}

5、消息应答与消息持久化

5.1、消息应答

 boolean autoAck = true;

 true:消息自动应答,当消息队列将消息分发给消费者,消息队列就会将内存中的这条消息删除,一旦正在处理消息的消费者宕机,会发生数据丢失。

false:消息手动应答,当消费者处理完消息后,返回ack给消息队列确认后,消息队列才会删除内存中的这条消息。

消息应答默认是打开的 false

5.2、消息持久化

        //声明消息队列
        boolean durable = false; //是否开启消息持久化,false不开启,true开启
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);

在RabbitMQ中,消息队列一旦被定义,是不可以修改参数的,如上消息队列定义为不开启消息持久化,如果将参数durable改为true,运行会报错,除非删掉该消息队列或者重新创建一个其他名称的消息队列

6、订阅模式 --------  一个消息被多个消费者消费

上图解读:

    ① 一个生产者,多个消费者

    ② 每个消费者都有自己的消息队列

    ③ 生产者将消息发送到Exchange交换机上,而不是之前直接发送到消息队列

    ④ 每个消息队列都要绑定到交换机上

    ⑤ 生产者将消息发送给交换机,经过消息队列,就可以实现一个消息被多个消费者消费

6.1、消息发布者

package com.zxj.pubsub;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 发布订阅模式 fanout 分发方式
 * 发布者
 * @author zxj
 * @date 2020/6/3 10:12
 */
public class Publish {

    private static final String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机,交换机没有存储能力
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发方式

        //发送消息
        String msg = "hello ps";
        channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());

        System.out.println("Pub msg : " + msg);

        channel.close();
        connection.close();
    }
}

 6.2、消息订阅者1和订阅者2

package com.zxj.pubsub;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 订阅者1
 * @author zxj
 * @date 2020/6/3 10:20
 */
public class Subscribe1 {

    private static final String EXCHANGE_NAME = "test_exchange_fanout";

    private static final String QUEUE_NAME = "test_exchange_fanout_email";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //将队列绑定到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1   Sub email : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}
package com.zxj.pubsub;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 订阅者2
 * @author zxj
 * @date 2020/6/3 10:20
 */
public class Subscribe2 {

    private static final String EXCHANGE_NAME = "test_exchange_fanout";

    private static final String QUEUE_NAME = "test_exchange_fanout_msg";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //将队列绑定到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("2   Sub msg : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }
}

 

7、Exchange(交换机)

7.1 、作用:接收生产者发布的消息,将消息推送到与之绑定的消息队列

7.2、匿名转发和非匿名转发

7.3、类型

7.3.1、Fanout类型(不处理路由键,只是简单的将消息队列绑到交换机上),接收到消息后,会转发给所有与之绑定的消息队列,类似广播,是消息转发最快的方式

代码实现 见 《6、订阅模式 --------  一个消息被多个消费者消费》

    

7.3.2、 Direct类型

    发布者的routingKey与订阅者的routingKey完全匹配,交换机才会转发消息到对应的消息队列

    

代码实现: 

生产者

package com.zxj.routing;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 路由模式 完全匹配路由键
 * @author zxj
 * @date 2020/6/3 10:48
 */
public class Send {

    private static final String EXCHANGE_NAME = "test_exchange_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");//路由模式

        String msg = "hello direct";

        //路由键
//        String routingKey = "error";
        String routingKey = "info";
//        String routingKey = "warning";
        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());

        System.out.println("Send msg : " + msg);

        channel.close();
        connection.close();

    }
}

消费者1和消费者2

package com.zxj.routing;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/3 10:51
 */
public class Recv1 {

    private static final String EXCHANGE_NAME = "test_exchange_direct";
    private static final String QUEUE_NAME = "test_queue_direct_1";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicQos(1);//公平

        //绑定队列到交换机
        String routingKey = "error";
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1   Recv1 msg : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}
package com.zxj.routing;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/3 10:51
 */
public class Recv2 {

    private static final String EXCHANGE_NAME = "test_exchange_direct";
    private static final String QUEUE_NAME = "test_queue_direct_2";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicQos(1);//公平

        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "warning");

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("2   Recv2 msg : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}

 

7.3.3、Topic类型   主题模式  

      路由键匹配模式

     #  一个或多个字符

     *  一个字符

        

          

    代码实现

    生产者

package com.zxj.topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * Topic 主题匹配模式
 * @author zxj
 * @date 2020/6/3 11:25
 */
public class Send {
    private static final String EXCHANGE_NAME = "test_exchange_topic";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");//路由模式

        String msg = "hello topic";

        //路由键
//        String routingKey = "goods.add"; // 消费者1,2都能收到
        String routingKey = "goods.delete";  // 消费者1收不到,消费者2 能收到
        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());

        System.out.println("Send msg : " + msg);

        channel.close();
        connection.close();

    }
}

   消费者1和消费者2

package com.zxj.topic;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 消费者1   只能接收 goods.add 路由键
 * @author zxj
 * @date 2020/6/3 11:26
 */
public class Recv1 {
    private static final String EXCHANGE_NAME = "test_exchange_topic";
    private static final String QUEUE_NAME = "test_queue_topic_1";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicQos(1);//公平

        //绑定队列到交换机
        String routingKey = "goods.add";
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1   Recv1 msg : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}
package com.zxj.topic;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 消费者2  能接收 goods.# 路由键
 * @author zxj
 * @date 2020/6/3 11:26
 */
public class Recv2 {
    private static final String EXCHANGE_NAME = "test_exchange_topic";
    private static final String QUEUE_NAME = "test_queue_topic_2";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicQos(1);//公平

        //绑定队列到交换机
        String routingKey = "goods.#";
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("2   Recv2 msg : " + new String(body, "utf-8"));
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        //监听消息队列
        boolean autoAck = false;
        channel.basicConsume(QUEUE_NAME, autoAck, consumer);
    }
}

 

8、RabbitMQ消息确认机制(事务 + confirm)

8.1 在rabbitmq中可以通过消息持久化,解决rabbitmq服务当即导致的数据丢失问题

8.2 rabbitmq 默认情况下不知道生产者发布的消息有没有到达rabbitmq服务器,处理方式两种:

        AMQP 实现了事务机制

        Confirm 模式  ---------    异步

8.3 事务机制    -------------   降低了RabbitMQ的吞吐量

    txSelect:用于将当前channel设置为开启事务模式

    txCommit:用于事务提交

    txRollback:用于回滚事务

    代码实现

    生产者

package com.zxj.transaction;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 事务机制  通过事务方式确认消息有没有到达,
 *
 * 缺点: 降低RabbitMQ的消息吞吐量
 *
 * @author zxj
 * @date 2020/6/3 11:40
 */
public class TxSend {
    private static final String QUEUE_NAME = "test_queue_tx";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        String msg = "hello tx";

        try {
            //channel 开启事务
            channel.txSelect();

            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

            //模拟异常
            int i = 1/0;

            //提交事务
            channel.txCommit();
        } catch (Exception e){
            //回滚事务
            channel.txRollback();
            System.out.println(" send msg txRollback !!!");
        }


        System.out.println("Send msg : " + msg);

        channel.close();
        connection.close();

    }
}

    消费者

package com.zxj.transaction;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/3 11:46
 */
public class TxRecv {
    private static final String QUEUE_NAME = "test_queue_tx";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("txRecv msg : " + new String(body, "utf-8"));
            }
        });
    }
}

8.4、Confirm模式  ---------    异步

    原理:

     生产者将channel设置成confirm模式,所有在该channel上发布的消息都会被分配一个唯一ID(从1开始),一旦消息被分发到指定的队列后,RabbitMQ服务器就会发送一个确认消息(包含消息的唯一ID)给生产者,这就使得生产者知道消息已经正确到达消息队列。

       如果开启了消息持久化,那么确认消息会在消息写入磁盘持久化成功后发送给生产者,确认消息中deliver-tag域包含了消息的序列号,此外,RabbitMQ服务器也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。

    模式:

      普通   发一条消息    waitForConfirm()

      批量   发一批消息    waitForConfirm()

      异步   提供回调方法

   代码实现:

   普通   发一条消息 

    生产者

package com.zxj.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 普通模式   发送一条
 * @author zxj
 * @date 2020/6/3 13:18
 */
public class SendNormal {

    private static final String QUEUE_NAME = "test_queue_confirm";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        //开启confirm普通模式
        channel.confirmSelect();

        String msg = "hello confirm";

        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

        if(!channel.waitForConfirms()){
            System.out.println("msg send failed");
        } else {
            System.out.println("msg send success");
        }

        System.out.println("Send msg : " + msg);

        channel.close();
        connection.close();

    }
}

  消费者

package com.zxj.confirm;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/3 13:22
 */
public class Recv {
    private static final String QUEUE_NAME = "test_queue_confirm";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("confirm Recv msg : " + new String(body, "utf-8"));
            }
        });
    }
}

   批量  发送一批消息

   生产者

package com.zxj.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 普通模式   发送一条
 * @author zxj
 * @date 2020/6/3 13:18
 */
public class SendBatch {

    private static final String QUEUE_NAME = "test_queue_confirmBatch";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        //开启confirm普通模式
        channel.confirmSelect();

        String msg = "hello confirmBatch";

        //批量发送
        for (int i = 0; i < 10; i++) {
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        }


        if(!channel.waitForConfirms()){
            System.out.println("batch msg send failed");
        } else {
            System.out.println("batch msg send success");
        }

        System.out.println("Send msg : " + msg);

        channel.close();
        connection.close();

    }
}

  消费者同 普通 模式

 

 异步模式 

  生产者

package com.zxj.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;

/**
 * 异步模式
 * 生产者维护一个SortedSet集合,每发送一条消息到Rabbtimq,同时王SortedSet集合中插入一个标记,
 * 当收到确认ack时,在集合中移除对应消息的标记
 * 当未收到ack消息时,根据业务做对应处理
 * @author zxj
 * @date 2020/6/3 13:18
 */
public class SendAsync {

    private static final String QUEUE_NAME = "test_queue_confirmAsync";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        //开启confirm普通模式
        channel.confirmSelect();

        //创建集合用于存储消息标记
        final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());

        //添加通道监听
        channel.addConfirmListener(new ConfirmListener() {
            //ok的ack
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                //移除集合中对应的标记
                if(multiple){
                    System.out.println(" handleAck   multiple ");
                    confirmSet.headSet(deliveryTag + 1).clear();
                } else {
                    System.out.println(" handleAck   un multiple ");
                    confirmSet.remove(deliveryTag);
                }
            }

            // 失败处理
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                //根据业务进行处理失败的数据,此处也做移除集合中对应的标记
                if(multiple){
                    System.out.println(" handleNack   multiple ");
                    confirmSet.headSet(deliveryTag + 1).clear();
                } else {
                    System.out.println(" handleNack   un multiple ");
                    confirmSet.remove(deliveryTag);
                }
            }
        });


        String msg = "hello confirm";

        while (true){
            long seqNo = channel.getNextPublishSeqNo();
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            System.out.println("Send msg : " + msg);
            confirmSet.add(seqNo);
        }


    }
}

消费者

package com.zxj.confirm;

import com.rabbitmq.client.*;
import com.zxj.utils.ConnectionUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author zxj
 * @date 2020/6/3 13:22
 */
public class Recv {
//    private static final String QUEUE_NAME = "test_queue_confirm";
//    private static final String QUEUE_NAME = "test_queue_confirmBatch";
    private static final String QUEUE_NAME = "test_queue_confirmAsync";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME, false,false,false, null);

        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("confirm Recv msg : " + new String(body, "utf-8"));
            }
        });
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值