RabbitMQ :概述,Web界面介绍,快速上手,工作模式

MQ和RabbitMQ的概述

什么是MQ

MQ就是消息队列(Message Queue),它是一种消息中间件,主要用于在不同系统,进程服务之间传递消息。

举例:就像我们在点外卖时,我们相当于生产者,外卖骑手相当于消费者,外卖平台则相当于MQ,这样订单就可以先排队,然后再由骑手慢慢的接单进行派送

MQ的核心作用

异步处理:

在业务的处理中,一些操作可能十分的耗时,但是不需要立即的进行返回,我们可以借助MQ对这些消息进行异步化,如:用户注册后发送消息或者邮箱进行通知

系统解耦:

系统之间通过消息交互,不直接的进行依赖,在传统的系统中,模块之间是经常进行直接调用的,但是这样会导致高耦合,系统稳定性差,使用MQ后,我们在中间加一层消息队列,就算其中一个系统挂了,消息仍然存储在MQ中,不会丢失。

削峰填谷:

把高并发的流量转为平稳的消费,在高并发系统中,请求量不稳定 ,有高峰有低谷,如果系统直接处理所有请求,可能会被瞬间打垮,引入MQ后,暂储存所有的请求,后端从队列中按系统能力稳定消费;短时间内请求暴增也不会冲垮数据库

可靠的传输:

确保消息不会丢失或重复。

RabbitMQ

而RabbitMQ就是MQ的一种具体实现,基于AMQP协议实现的消息队列

AMQP协议:是一种面向消息中间件的通信协议标准

RabbitMQ的工作流程

RabbitMQ是一个消息中间键,也是一个消费者生产者模型,负责接收,存储并转发消息

Producer:生产者,也是RabbitMQ Server的客户端,向RabbitMQ发送消息

Customer:消费者,也是RabbitMQ Server的客户端,向RabbitMQ接收(消费)消息

Broker:接收和存储,路由,传递消息的核心组件,相当于一个消息的中转站

Connection:是属于客户端和RabbitMQ服务器之间的一个TCP连接,负责传送客户端和服务器之间的所有数据和控制信息

Channel:属于Connection上的一个抽象层,在RabbitMQ中,一个TCP有多个Channel,每个Channel都是独⽴的虚拟连接.消息的发送和接收都是基于Channel的

Virtual host(虚拟机):为消息队列提供了一种逻辑上的隔离机制,是 RabbitMQ 内部用于隔离不同应用或租户的独立命名空间,Broker是一栋大楼,而虚拟机就是这栋大楼里的独立房间**。**

Queue(队列):是RabbitMQ中的内部对象,用于存储消息,多个消费者可以订阅同一个队列

Exchange(交换机):它负责接收⽣产者发送的消息,并根据特定的规则 把这些消息路由到⼀个或多个Queue列中

虚拟机,交换机,队列之间的关系

Web页面

RabbitMQ的快速上手

引入其依赖

java 复制代码
<dependency>
     <groupId>com.rabbitmq</groupId>
     <artifactId>amqp-client</artifactId>
     <version>5.7.3</version>
 </dependency>

生产者(Producer)

java 复制代码
public class producerDemo {
    public static void main(String[] args) throws IOException, TimeoutException {
//        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost("你的IP地址");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("你的用户名");
        connectionFactory.setPassword("你的密码");
        connectionFactory.setVirtualHost("你的虚拟机");

        Connection connection= connectionFactory.newConnection();

//        2.开启信道
        Channel channel=connection.createChannel();
//        申明队列,使用的是内置的交换机
//        如果队列不存在,则创建队列,否则则不创建
        channel.queueDeclare("666",true,false,false,null);
        for (int i = 0; i < 8; i++) {
            String meg="测试一下消息队列的生产者";
            channel.basicPublish("","666",null,meg.getBytes());
        }
        System.out.println("消息发送成功");
        channel.close();
        connection.close();
    }
}

消费者(Customer)

java 复制代码
public class ConsumerDemo {
    public static void main(String[] args) throws IOException, TimeoutException {
//        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost("你的IP地址");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("你的用户名");
        connectionFactory.setPassword("你的密码");
        connectionFactory.setVirtualHost("你的虚拟机");

        Connection connection= connectionFactory.newConnection();

//        2.创建channel
        Channel channel=connection.createChannel();

//        3.声明队列
//        channel.queueDeclare("666",true,false,false,null);
//        4.消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收消息"+new String(body));
            }
        };

        channel.basicConsume("666",true,consumer);
        System.out.println("消费消息成功");
//        5.释放资源
        channel.close();
        connection.close();
    }
}

生产者

消费者

在web页面中队列的显示

RabbitMQ的七种工作模式

1.简单模式

2.工作队列

⼀个⽣产者P,多个消费者C1,C2.在多个消息的情况下,队列会将消息分派给不同的消费者,每个消费者都会接收到不同的消息

3.Publish/Subscribe (发布订阅模式)

⽣产者将消息发送到Exchange,由交换机将消息按⼀定规则路由到⼀个或多个队列中

生产者

java 复制代码
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //     1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();

        //      3.申明交换机
        channel.exchangeDeclare(Constants.EXCHANGE_NAME, BuiltinExchangeType.FANOUT,true);

//        申明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE_NAME1,true,false,false,null);
        channel.queueDeclare(Constants.FANOUT_QUEUE_NAME2,true,false,false,null);
//        交换机和队列进行绑定
        channel.queueBind(Constants.FANOUT_QUEUE_NAME1,Constants.EXCHANGE_NAME,"");
        channel.queueBind(Constants.FANOUT_QUEUE_NAME2,Constants.EXCHANGE_NAME,"");
//        发送消息
        String msg="消息发送成功";
        channel.basicPublish("",Constants.EXCHANGE_NAME,null,msg.getBytes());
//        消息发送成功
        System.out.println("消息发送成功");
        channel.close();
        connection.close();
    }

}

消费者1

java 复制代码
public class customer1 {

    public static void main(String[] args) throws IOException, TimeoutException {
        //     1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//              3. 申明交换机
        channel.exchangeDeclare(Constants.EXCHANGE_NAME, BuiltinExchangeType.FANOUT,true);
//              4.申明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE_NAME1,true,false,false,null);
//              5.消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费消息+fanout+queue_name1"+new String(body));
            }
        };
        channel.basicConsume(Constants.FANOUT_QUEUE_NAME1,true,consumer);
        System.out.println("消息消费成功");
    }
}

消费者2

java 复制代码
public class customer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //     1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//        申明交换机
        channel.exchangeDeclare(Constants.EXCHANGE_NAME, BuiltinExchangeType.FANOUT,true);
//        申明队列
        channel.queueDeclare(Constants.FANOUT_QUEUE_NAME2,true,false,false,null);
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消息消费成功 fanout queue_name2"+new String(body));
            }
        };
        channel.basicConsume(Constants.FANOUT_QUEUE_NAME2,true,consumer);
        System.out.println("消息消费成功");


    }
}

4.Routing(路由模式)

生产者

java 复制代码
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
//        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();

//        申明交换机
        channel.exchangeDeclare(Constants.DIRECT_EXCHANGE_NAME,BuiltinExchangeType.DIRECT,true);

//        申明队列
        channel.queueDeclare(Constants.DIRECT_QUEUE_NAME1,true,false,false,null);
        channel.queueDeclare(Constants.DIRECT_QUEUE_NAME2,true,false,false,null);

//        队列的绑定
        channel.queueBind(Constants.DIRECT_QUEUE_NAME1,Constants.DIRECT_EXCHANGE_NAME,"a");
        channel.queueBind(Constants.DIRECT_QUEUE_NAME2,Constants.DIRECT_EXCHANGE_NAME,"a");
        channel.queueBind(Constants.DIRECT_QUEUE_NAME2,Constants.DIRECT_EXCHANGE_NAME,"b");
        channel.queueBind(Constants.DIRECT_QUEUE_NAME2,Constants.DIRECT_EXCHANGE_NAME,"c");

        String msg="消息的发送 + direct +  a   ";
        String msg1="消息的发送 + direct +  b   ";
        String msg2="消息的发送 + direct +  c   ";

        channel.basicPublish(Constants.DIRECT_EXCHANGE_NAME,"a",null,msg.getBytes());
        channel.basicPublish(Constants.DIRECT_EXCHANGE_NAME,"b",null,msg1.getBytes());
        channel.basicPublish(Constants.DIRECT_EXCHANGE_NAME,"c",null,msg2.getBytes());
//        System.out.println("消息发送成功");
        channel.close();
        connection.close();

    }
}

消费者1

java 复制代码
public class Customer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//        创建队列
        channel.queueDeclare(Constants.DIRECT_QUEUE_NAME1,true,false,false,null);

//        消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("已经接收到消息"+new String(body));
            }
        };
        channel.basicConsume(Constants.DIRECT_QUEUE_NAME1,true,consumer);
//        System.out.println("消息消费成功");
    }
}

消费者2

java 复制代码
public class Customer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//        创建队列
        channel.queueDeclare(Constants.DIRECT_QUEUE_NAME2,true,false,false,null);

//        消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("已经接收到消息"+new String(body));
            }
        };
        channel.basicConsume(Constants.DIRECT_QUEUE_NAME2,true,consumer);
//        System.out.println("消息消费成功");
    }
}

5.Topics(通配符模式)

生产者

java 复制代码
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {

        //        1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();

//        申明交换机
        channel.exchangeDeclare(Constants.TOPIC_EXCHANGE_NAME, BuiltinExchangeType.TOPIC,true);

//        申明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE_NAME1,true,false,false,null);
        channel.queueDeclare(Constants.TOPIC_QUEUE_NAME2,true,false,false,null);

//        队列的绑定
        channel.queueBind(Constants.TOPIC_QUEUE_NAME1,Constants.TOPIC_EXCHANGE_NAME,"*.a.*");
        channel.queueBind(Constants.TOPIC_QUEUE_NAME2,Constants.TOPIC_EXCHANGE_NAME,"*.*.b");
        channel.queueBind(Constants.TOPIC_QUEUE_NAME2,Constants.TOPIC_EXCHANGE_NAME,"c.#");

        String msg="消息的发送 + direct +  ae.a.f  ";
        String msg1="消息的发送 + direct +  c.a.b  ";
        String msg2="消息的发送 + direct +  c.e.f  ";

        channel.basicPublish(Constants.TOPIC_EXCHANGE_NAME,"ae.a.f",null,msg.getBytes());
        channel.basicPublish(Constants.TOPIC_EXCHANGE_NAME,"c.a.b",null,msg1.getBytes());
        channel.basicPublish(Constants.TOPIC_EXCHANGE_NAME,"c.e.f",null,msg2.getBytes());
        channel.close();
        connection.close();
    }
}

消费者1

java 复制代码
public class Customer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //     1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//              3. 申明交换机
        channel.exchangeDeclare(Constants.TOPIC_EXCHANGE_NAME, BuiltinExchangeType.TOPIC,true);
//              4.申明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE_NAME1,true,false,false,null);
//              5.消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费消息+fanout+queue_name1"+new String(body));
            }
        };
        channel.basicConsume(Constants.TOPIC_QUEUE_NAME1,true,consumer);
    }
}

消费者2

java 复制代码
public class Customer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //     1.建立连接
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();
//              3. 申明交换机
        channel.exchangeDeclare(Constants.TOPIC_EXCHANGE_NAME, BuiltinExchangeType.TOPIC,true);
//              4.申明队列
        channel.queueDeclare(Constants.TOPIC_QUEUE_NAME2,true,false,false,null);
//              5.消费消息
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费消息+fanout+queue_name1"+new String(body));
            }
        };
        channel.basicConsume(Constants.TOPIC_QUEUE_NAME2,true,consumer);
    }
}

6.RPC通信

生产者

客户端在发送消息之前:

会生成一个唯一的correlationId,用于标记此次请求

创建一个匿名回调队列

发布一条消息到请求队列中,并且进行消息属性的设置

replyTo:回调队列的名字

correlationId:请求Id

创建一个阻塞的队列,用于存储回调结果

接收回调消息

在接受的时候,要先判断返回来的correlationId是否和之前创建队列属性中的correlationId相同

java 复制代码
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();

        channel.queueDeclare(Constants.RPC_REQUEST_QUEUE,true,false,false,null);

        String cotId= UUID.randomUUID().toString();
        String msg="rpc通信通信的  消息的发送";
        String replyQueueName=channel.queueDeclare().getQueue();

//      消息的发送
        AMQP.BasicProperties properties=new AMQP.BasicProperties()
                .builder()
                .correlationId(cotId)
                .replyTo(replyQueueName)
                .build();
        channel.basicPublish("",Constants.RPC_REQUEST_QUEUE,properties,msg.getBytes(StandardCharsets.UTF_8));

        System.out.println("消息发送成功");
//        阻塞队列,用于存储回调的结果
        final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);

        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到的消息"+new String(body));
                if(cotId.equals(properties.getCorrelationId())){
                    response.offer(new String(body,"UTF-8"));

                }
            }
        };
//        回调消息的接收
        channel.basicConsume(replyQueueName,true,consumer);
//          获得回调的结果
        String take = response.take();
        System.out.println("[RPC客户端接收]"+take);
        channel.close();
        connection.close();
    }
}

消费者

用于控制每次最多可以同时接收多少消息

java 复制代码
public class Customer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);

        Connection connection= connectionFactory.newConnection();

        //        2.开启信道
        Channel channel=connection.createChannel();


        channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE,true,false,false,null);
//        接收消息
//        最多只可以接收一个消息
        channel.basicQos(1);
        System.out.println("Awaiting RPC request");
        DefaultConsumer consumer=new DefaultConsumer(channel){

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                AMQP.BasicProperties basicProperties=new AMQP.BasicProperties()
                        .builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                System.out.println("消息已经成功的收到");

//                生成返回
                String message=new String(body);
                String response="request"+message+"response"+"处理成功";

                channel.basicPublish("", properties.getReplyTo(),basicProperties,response.getBytes(StandardCharsets.UTF_8));
//                手动对消息进行应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(Constants.RPC_REQUEST_QUEUE,false,consumer);
    }
}

7.Publisher Confirms(发布确认)

作为消息的中间件,时刻会面临着消息丢失的问题,所以我们需要消息确认机制,来确定消息是否发送成功

消息丢失的情况:

  • 网络闪断、Broker 挂了;

  • 消息没发送成功;

  • Broker 收到消息但崩溃还没落盘

开启confirm模式

java 复制代码
channel.confirmSelect();

一旦开启后,RabbitMQ会对每条消息返回一个确认(ACK)或否认(NACK)信号

java 复制代码
channel.waitForConfirmsOrDie(5000);

等待RabbitMQ对之前的消息进行返回,如果这段时间没有进行返回,就会抛出异常

单独确认

就是一条消息一条消息的进行确认

java 复制代码
//   单独确认
    private static void publishingMessagesIndividually() throws Exception{
        try(Connection connection=createConnection()) {
//            创建信道
            Channel channel=connection.createChannel();

//            设置信道为confirm模式
            channel.confirmSelect();
//            声明信道
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE1,true,false,false,null);

//            发送消息
            String msg="单独确认进行消息的发送";
            long start=System.currentTimeMillis();
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                System.out.println("confirms111"+i);

                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE1,null,msg.getBytes(StandardCharsets.UTF_8));
                channel.waitForConfirmsOrDie(5000);
            }

            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }

批量确认

当消息到达一定的量时,才对消息进行确认

java 复制代码
//    批量确认
    private static void publishingMessagesInBatches()throws Exception {
        try(Connection connection=createConnection()) {
//            创建信道
            Channel channel=connection.createChannel();
//            创建信道为confirm模式
            channel.confirmSelect();
//            申明队列
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE2,true,false,false,null);

//            发送消息
            int batchSize=100;
            int outStandingMessageCount=0;
            long start=System.currentTimeMillis();
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg="confirm222"+i;
                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE2,null,msg.getBytes());
                if(outStandingMessageCount==batchSize){
                    channel.waitForConfirmsOrDie(5000);
                    outStandingMessageCount=0;
                }
                outStandingMessageCount++;
            }
            if(outStandingMessageCount>0){
                channel.waitForConfirmsOrDie(5000);
            }
            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }

异步确认

用一个线程安全的 SortedSet 来跟踪还未被确认(ack/nack)的消息

注册确定监听器

ConfirmListener是一个接口,用于接收来自RabbitMQ的消息确认回调

java 复制代码
//    异步确认
    private static void handlingPublisherConfirmsAsynchronously()throws Exception {
        try(Connection connection=createConnection()) {
            Channel channel=connection.createChannel();
//            创建信道模式为confirm
            channel.confirmSelect();
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE3,true,false,false,null);
            long start=System.currentTimeMillis();
            SortedSet<Object> confirm = Collections.synchronizedSortedSet(new TreeSet<>());

            channel.addConfirmListener(new ConfirmListener() {
                @Override
                public void handleAck(long deliverTag, boolean multiple) throws IOException {
                    if(multiple){
                        confirm.headSet(deliverTag+1).clear();
                    }else{
                        confirm.remove(deliverTag);
                    }

                }

                @Override
                public void handleNack(long deliverTag, boolean multiple) throws IOException {
                    if(multiple){
                        confirm.headSet(deliverTag+1).clear();
                    }else{
                        confirm.remove(deliverTag);
                    }

                }
            });

//            发送消息
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg="confirm333"+i;
                long seqNo=channel.getNextPublishSeqNo();
                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE3,null,msg.getBytes());
                confirm.add(seqNo);
            }
            while (!confirm.isEmpty()){
                Thread.sleep(10);
            }
            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }

所有的消息确认的代码

java 复制代码
public class PublishConfirm {

    private static final Integer MESSAGE_COUNT=1000;

    static Connection createConnection() throws IOException, TimeoutException {
        ConnectionFactory connectionFactory=new ConnectionFactory();
        connectionFactory.setHost(Constants.HOST);
        connectionFactory.setPort(Constants.PORT);
        connectionFactory.setUsername(Constants.USER_NAME);
        connectionFactory.setPassword(Constants.PASSWORD);
        connectionFactory.setVirtualHost(Constants.VIRTUAL);
        return connectionFactory.newConnection();
    }




    public static void main(String[] args) throws Exception {
//        单独确认
        publishingMessagesIndividually();

//        批量确认
        publishingMessagesInBatches();

//        异步确认
        handlingPublisherConfirmsAsynchronously();

    }

//    异步确认
    private static void handlingPublisherConfirmsAsynchronously()throws Exception {
        try(Connection connection=createConnection()) {
            Channel channel=connection.createChannel();
//            创建信道模式为confirm
            channel.confirmSelect();
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE3,true,false,false,null);
            long start=System.currentTimeMillis();
            SortedSet<Object> confirm = Collections.synchronizedSortedSet(new TreeSet<>());

            channel.addConfirmListener(new ConfirmListener() {
                @Override
                public void handleAck(long deliverTag, boolean multiple) throws IOException {
                    if(multiple){
                        confirm.headSet(deliverTag+1).clear();
                    }else{
                        confirm.remove(deliverTag);
                    }

                }

                @Override
                public void handleNack(long deliverTag, boolean multiple) throws IOException {
                    if(multiple){
                        confirm.headSet(deliverTag+1).clear();
                    }else{
                        confirm.remove(deliverTag);
                    }

                }
            });

//            发送消息
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg="confirm333"+i;
                long seqNo=channel.getNextPublishSeqNo();
                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE3,null,msg.getBytes());
                confirm.add(seqNo);
            }
            while (!confirm.isEmpty()){
                Thread.sleep(10);
            }
            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }

//    批量确认
    private static void publishingMessagesInBatches()throws Exception {
        try(Connection connection=createConnection()) {
//            创建信道
            Channel channel=connection.createChannel();
//            创建信道为confirm模式
            channel.confirmSelect();
//            申明队列
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE2,true,false,false,null);

//            发送消息
            int batchSize=100;
            int outStandingMessageCount=0;
            long start=System.currentTimeMillis();
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String msg="confirm222"+i;
                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE2,null,msg.getBytes());
                if(outStandingMessageCount==batchSize){
                    channel.waitForConfirmsOrDie(5000);
                    outStandingMessageCount=0;
                }
                outStandingMessageCount++;
            }
            if(outStandingMessageCount>0){
                channel.waitForConfirmsOrDie(5000);
            }
            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }


    //   单独确认
    private static void publishingMessagesIndividually() throws Exception{
        try(Connection connection=createConnection()) {
//            创建信道
            Channel channel=connection.createChannel();

//            设置信道为confirm模式
            channel.confirmSelect();
//            声明信道
            channel.queueDeclare(Constants.PUBLISHER_CONFIRMS_QUEUE1,true,false,false,null);

//            发送消息
            String msg="单独确认进行消息的发送";
            long start=System.currentTimeMillis();
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                System.out.println("confirms111"+i);

                channel.basicPublish("",Constants.PUBLISHER_CONFIRMS_QUEUE1,null,msg.getBytes(StandardCharsets.UTF_8));
                channel.waitForConfirmsOrDie(5000);
            }

            long end=System.currentTimeMillis();
            System.out.println("耗时"+(end-start));
        }
    }
}
相关推荐
兜兜风d'3 小时前
RabbitMQ 持久性详解
spring boot·分布式·rabbitmq·1024程序员节
何中应3 小时前
如何截取PDF内容为图片
java·开发语言·后端·pdf
哈皮Superman4 小时前
【Research】MagicFuzzer: Scalable deadlock detection for large-scale applications
java·开发语言·数据库
Empty_7775 小时前
Ceph分布式存储
分布式·ceph
I'm Jie5 小时前
(二)Gradle 依赖仓库及安全凭证配置
java·spring boot·spring·gradle·maven
有谁看见我的剑了?5 小时前
Rocky 9 安装 Elasticsearch分布式集群基于非安全特性
分布式·安全·elasticsearch
牢七5 小时前
CATWIFI
java
信码由缰5 小时前
单体架构中的事件驱动架构:Java应用程序的渐进式重构
java
初学小白...5 小时前
实现Runnable接口
java·开发语言