RabbitMQ --- 六种工作模式(一)

在这里插入图片描述

在这里插入图片描述

RabbitMQ(一)

点击链接,查看上一篇,rabbitmq的安装及基本概念: RabbitMQ(一)

一、rabbitmq六种工作模式

1.1 简单模式

在这里插入图片描述
RabbitMQ是一个消息中间件,你可以想象它是一个邮局。当你把信件放到邮箱里时,能够确信邮递员会正确地递送你的信件。RabbitMq就是一个邮箱、一个邮局和一个邮递员。

  • 发送消息的程序是生产者
  • 队列就代表一个邮箱。虽然消息会流经RbbitMQ和你的应用程序,但消息只能被存储在队列里。队列存储空间只受服务器内存和磁盘限制,它本质上是一个大的消息缓冲区。多个生产者可以向同一个队列发送消息,多个消费者也可以从同一个队列接收消息.
  • 消费者等待从队列接收消息

在这里插入图片描述

1.1.1 创建Maven项目

在这里插入图片描述

1.1.2 pom.xml

添加 slf4j 依赖, 和 rabbitmq amqp 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.tedu</groupId>
    <artifactId>rabbitmq</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.8.0-alpha2</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.8.0-alpha2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

1.1.3 生产者发送消息

  1. 连接rabbitmq服务器
  2. 定义队列
  3. 发送消息
  4. 关闭连接
package m1_simple;

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

public class Producer {
    public static void main(String[] args) throws Exception {
        //1.连接rabbitmq服务器
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");

        // 用谁的服务器,设置一个自己的虚拟主机,用虚拟机的话就不必操作这行代码
        //f.setVirtualHost("/lx");
        /*
         * 与rabbitmq服务器建立连接,
         * rabbitmq服务器端使用的是nio,会复用tcp连接,
         * 并开辟多个信道与客户端通信
         * 以减轻服务器端建立连接的开销
         */
        Connection con = f.newConnection();
        //建立信道
        Channel c = con.createChannel();

        //2.定义队列(会通知服务器想使用一个“helloworld队列”,服务器会找到这个队列,如果不存在,服务器会新建队列)
        /*
         * 声明队列,会在rabbitmq中创建一个队列
         * 如果已经创建过该队列,就不能再使用其他参数来创建
         *
         * 参数含义:
         *   -queue: 队列名称
         *   -durable: 队列持久化,true表示RabbitMQ重启后队列仍存在
         *   -exclusive: 排他,true表示限制仅当前连接可用
         *   -autoDelete: 当最后一个消费者断开后,是否删除队列
         *   -arguments: 其他参数
         */
        c.queueDeclare("helloworld111",false,false,false,null);

        //3.发送消息
        /*
         * 发布消息
         * 这里把消息向默认交换机发送.
         * 默认交换机隐含与所有队列绑定,routing key即为队列名称
         *
         * 参数含义:
         *  -exchange: 交换机名称,空串表示默认交换机"(AMQP default)",不能用 null
         * 	-routingKey: 对于默认交换机,路由键就是目标队列名称
         * 	-props: 其他参数,例如头信息
         * 	-body: 消息内容byte[]数组
         */
        c.basicPublish("","helloworld",null,"hello".getBytes());
        System.out.println("消息已发送!!");
        //4.断开链接
        c.close();
        con.close();
    }
}

1.1.4 消费者接收消息

package m1_simple;

import com.rabbitmq.client.*;

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

public class Consumer {
    public static void main(String[] args) throws Exception {
        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");
        f.setPort(5672);//默认端口,可以不设置
        f.setUsername("admin");
        f.setPassword("admin");

        //建立通道
        Channel c = f.newConnection().createChannel();
        //2.定义队列
        c.queueDeclare("helloworld",false,false,false,null);

        //4.创建回调对象
        //4.处理消息的回调
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                //处理消息
                byte[] body = message.getBody();
                //把消息还原成字符串
                String msg = new String(body);
                System.out.println("收到:"+msg);
            }
        };
        //5.取消消息的回调
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };

        //3.从 helloworld 队列接收消息,消费消息
        c.basicConsume("helloworld",true,deliverCallback,cancelCallback);

    }
}

1.2 工作模式

在这里插入图片描述
在这里插入图片描述
工作队列(即任务队列)背后的主要思想是避免立即执行资源密集型任务,并且必须等待它完成。相反,我们将任务安排在稍后完成。

我们将任务封装为消息并将其发送到队列。后台运行的工作进程将获取任务并最终执行任务。当运行多个消费者时,任务将在它们之间分发。

使用任务队列的一个优点是能够轻松地并行工作。如果我们正在积压工作任务,我们可以添加更多工作进程,这样就可以轻松扩展。

1.2.1生产者发送消息

这里模拟耗时任务,发送的消息中,每个点使工作进程暂停一秒钟,例如"Hello…"将花费3秒钟来处理

package m2_work;

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

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

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {

        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");

        //建立通道
        Channel c = f.newConnection().createChannel();
        //2.定义队列
        c.queueDeclare("helloworld",false,false,false,null);
        //3.发消息
        while (true) {
            System.out.println("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            c.basicPublish("", "helloworld", null, msg.getBytes());
        }
    }
}

在这里插入图片描述

1.2.2 消费者接收消息

package m2_work;

import com.rabbitmq.client.*;

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

public class Consumer {
    public static void main(String[] args) throws Exception {
        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");
        //建立通道
        Channel c = f.newConnection().createChannel();

        //2.定义队列
        c.queueDeclare("helloworld",false,false,false,null);

        //4.建立回调对象
        //4.处理消息的回调
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
                for (int i=0;i<msg.length();i++){
                    if (msg.charAt(i)=='.'){
                        try {
                            Thread.sleep(1000);
                        }catch (InterruptedException e){

                        }
                    }
                }
                System.out.println("消息处理完成\n");
            }
        };
        //5.取消消息的回调
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };

        //3.开始消费数据
        c.basicConsume("helloworld",true,deliverCallback,cancelCallback);
    }
}

在这里插入图片描述

工作模式:多次启动Consumer测试,
在eclipse或者STS里面的话,直接多次run就行,但是在idea里面需要设置,如图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.3 启动多个消费者测试

(自己注意观察,这种情况是平均分配)

运行:

  • 一个生产者
  • 两个消费者

生产者发送多条消息,如: 1,2,3,4,5. 两个消费者分别收到:

  • 消费者一: 1,3,5
  • 消费者二: 2,4

rabbitmq在所有消费者中 轮询分发 消息,把消息均匀地发送给所有消费者
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.3 消息确认(Ack)

一个消费者接收消息后,在消息没有完全处理完时就挂掉了,那么这时会发生什么呢?

就现在的代码来说,rabbitmq把消息发送给消费者后,会立即删除消息,那么消费者挂掉后,它没来得及处理的消息就会丢失

如果生产者发送以下消息:

1……

2

3

4

5

两个消费者分别收到:

  • 消费者一: 1…, 3, 5
  • 消费者二: 2, 4

当消费者一收到所有消息后,要话费7秒时间来处理第一条消息,这期间如果关闭该消费者,那么1未处理>完成,3,5则没有被处理

我们并不想丢失任何消息, 如果一个消费者挂掉,我们想把它的任务消息派发给其他消费者

为了确保消息不会丢失,rabbitmq支持消息确认(回执)。当一个消息被消费者接收到并且执行完成后,消费者会发送一个ack (acknowledgment) 给rabbitmq服务器, 告诉他我已经执行完成了,你可以把这条消息删除了。

如果一个消费者没有返回消息确认就挂掉了(信道关闭,连接关闭或者TCP链接丢失),rabbitmq就会明白,这个消息没有被处理完成,rebbitmq就会把这条消息重新放入队列,如果在这时有其他的消费者在线,那么rabbitmq就会迅速的把这条消息传递给其他的消费者,这样就确保了没有消息会丢失。

这里不存在消息超时, rabbitmq只在消费者挂掉时重新分派消息, 即使消费者花非常久的时间来处理消息也可以

手动消息确认默认是开启的,前面的例子我们通过autoAck=ture把它关闭了。我们现在要把它设置为false,然后工作进程处理完意向任务时,发送一个消息确认(回执)。

消费者设置 ACK

  1. 开始消费数据时,设置手动ACK:
c.basicConsume(队列, false, 回调对象。。。)
  1. 处理完消息后,要发送回执
回调对象中处理完消息后:
c.basicAck(消息回执, 是否确认多条消息);
package m2_work;

import com.rabbitmq.client.*;

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

public class Consumer {
    public static void main(String[] args) throws Exception {
        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");
        //建立通道
        Channel c = f.newConnection().createChannel();

        //2.定义队列
        c.queueDeclare("helloworld",false,false,false,null);

        //4.建立回调对象
        //4.处理消息的回调
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
                for (int i=0;i<msg.length();i++){
                    if (msg.charAt(i)=='.'){
                        try {
                            Thread.sleep(1000);
                        }catch (InterruptedException e){

                        }
                    }
                }
                ///
                //消息回执数据
                //false表示不确认多条回执数据,只确认当前这一条
                c.basicAck(message.getEnvelope().getDeliveryTag(),false);
                ///
                System.out.println("消息处理完成\n");
            }
        };
        //5.取消消息的回调
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };

        //3.消费数据
        ///
        c.basicConsume("helloworld",false,deliverCallback,cancelCallback);
        System.out.println("开始消费数据");
        ///
    }
}

使用以上代码,就算杀掉一个正在处理消息的工作进程也不会丢失任何消息,工作进程挂掉之后,没有确认的消息就会被自动重新传递。

忘记确认(ack)是一个常见的错误, 这样后果是很严重的, 由于未确认的消息不会被释放, rabbitmq会吃掉越来越多的内存
可以使用下面命令打印工作队列中未确认消息的数量

rabbitmqctl list_queues name messages_ready messages_unacknowledged

当处理消息时异常中断, 可以选择让消息重回队列重新发送.
nack 操作可以是消息重回队列, 可以使用 basicNack() 方法:

// requeue为true时重回队列, 反之消息被丢弃或被发送到死信队列
c.basicNack(tag, multiple, requeue)

1.2.4 合理分发(Qos)

rabbitmq会一次把多个消息分发给消费者, 这样可能造成有的消费者非常繁忙, 而其它消费者空闲. 而rabbitmq对此一无所知, 仍然会均匀的分发消息

我们可以使用 basicQos(1) 方法, 这告诉rabbitmq一次只向消费者发送一条消息, 在返回确认回执前, 不要向消费者发送新消息. 而是把消息发给下一个空闲的消费者
在这里插入图片描述
在这里插入图片描述

1.2.5 消息持久化

当rabbitmq关闭时, 我们队列中的消息仍然会丢失, 除非明确要求它不要丢失数据

要求rabbitmq不丢失数据要做如下两点: 把队列和消息都设置为可持久化(durable)

队列设置为可持久化, 可以在定义队列时指定参数durable为true

//第二个参数是持久化参数durable
ch.queueDeclare("helloworld", true, false, false, null);

由于之前我们已经定义过队列"hello"是不可持久化的, 对已存在的队列, rabbitmq不允许对其定义不同的参数, 否则会出错, 所以这里我们定义一个不同名字的队列"task_queue"

//定义一个新的队列,名为 task_queue
//第二个参数是持久化参数 durable
ch.queueDeclare("task_queue", true, false, false, null);

生产者和消费者代码都要修改

这样即使rabbitmq重新启动, 队列也不会丢失. 现在我们再设置队列中消息的持久化, 使用MessageProperties.PERSISTENT_TEXT_PLAIN参数

消息本身也要设置成持久消息
c.basicPublish("", "helloworld", MessageProperties.PERSISTENT_TEXT_PLAIN, 消息)

//第三个参数设置消息持久化
ch.basicPublish("", "task_queue",
            MessageProperties.PERSISTENT_TEXT_PLAIN,
            msg.getBytes());

下面是"工作模式"最终完成的生产者和消费者代码

生产者代码

package m2_work;

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

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

public class Producer {
    public static void main(String[] args) throws Exception {

        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");

        //建立通道
        Channel c = f.newConnection().createChannel();
        //2.定义队列
        //c.queueDeclare("helloworld",false,false,false,null);
        //持久队列
        c.queueDeclare("test_queue",true,false,false,null);
        //3.发消息
        while (true) {
            System.out.println("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            //c.basicPublish("", "helloworld", null, msg.getBytes());
            c.basicPublish("", "test_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
        }
    }
}

消费者代码

package m2_work;

import com.rabbitmq.client.*;

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

public class Consumer {
    public static void main(String[] args) throws Exception {
        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");
        //建立通道
        Channel c = f.newConnection().createChannel();

        //2.定义队列
        //c.queueDeclare("helloworld",false,false,false,null);
        c.queueDeclare("test_queue",true,false,false,null);

        //4.建立回调对象
        //4.处理消息的回调
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws IOException {
                String msg = new String(message.getBody());
                System.out.println("收到:"+msg);
                for (int i=0;i<msg.length();i++){
                    if (msg.charAt(i)=='.'){
                        try {
                            Thread.sleep(1000);
                        }catch (InterruptedException e){

                        }
                    }
                }
                //消息回执数据
                //false表示不确认多条回执数据,只确认当前这一条
                c.basicAck(message.getEnvelope().getDeliveryTag(),false);
                System.out.println("消息处理完成\n");
            }
        };
        //5.取消消息的回调
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {

            }
        };

        /**
         * Qos:Quality of Service
         * 理解:每次抓取的消息数量
         * 如果设置成1,每次只抓取一条消息,这条消息处理完之前,不会继续抓取下一条
         * 必须在手动的Ack模式下才有效
         */
        c.basicQos(1);

        //3.消费数据
        //c.basicConsume("helloworld",false,deliverCallback,cancelCallback);
        c.basicConsume("test_queue",false,deliverCallback,cancelCallback);
        System.out.println("开始消费数据");
    }
}

1.3 发布订阅模式

在这里插入图片描述
在这里插入图片描述
在前面的例子中,我们任务消息只交付给一个工作进程。在这部分,我们将做一些完全不同的事情——我们将向多个消费者传递同一条消息。这种模式称为“发布/订阅”。

为了说明该模式,我们将构建一个简单的日志系统。它将由两个程序组成——第一个程序将发出日志消息,第二个程序接收它们。

在我们的日志系统中,接收程序的每个运行副本都将获得消息。这样,我们就可以运行一个消费者并将日志保存到磁盘; 同时我们可以运行另一个消费者在屏幕上打印日志。

最终, 消息会被广播到所有消息接受者

1.3.1 Exchanges 交换机

RabbitMQ消息传递模型的核心思想是,生产者永远不会将任何消息直接发送到队列。实际上,通常生产者甚至不知道消息是否会被传递到任何队列。

相反,生产者只能向交换机(Exchange)发送消息。交换机是一个非常简单的东西。一边接收来自生产者的消息,另一边将消息推送到队列。交换器必须确切地知道如何处理它接收到的消息。它应该被添加到一个特定的队列中吗?它应该添加到多个队列中吗?或者它应该被丢弃。这些规则由exchange的类型定义。

有几种可用的交换类型:direct、topic、header和fanout。我们将关注最后一个——fanout。让我们创建一个这种类型的交换机,并称之为 logs: ch.exchangeDeclare("logs", "fanout");

fanout交换机非常简单。它只是将接收到的所有消息广播给它所知道的所有队列。这正是我们的日志系统所需要的。

我们前面使用的队列具有特定的名称(还记得hello和task_queue吗?)能够为队列命名对我们来说至关重要——我们需要将工作进程指向同一个队列,在生产者和消费者之间共享队列。

但日志记录案例不是这种情况。我们想要接收所有的日志消息,而不仅仅是其中的一部分。我们还只对当前的最新消息感兴趣,而不是旧消息。

要解决这个问题,我们需要两件事。首先,每当我们连接到Rabbitmq时,我们需要一个新的空队列。为此,我们可以创建一个具有随机名称的队列,或者,更好的方法是让服务器为我们选择一个随机队列名称。其次,一旦断开与使用者的连接,队列就会自动删除。在Java客户端中,当我们不向queueDeclare()提供任何参数时,会创建一个具有生成名称的、非持久的、独占的、自动删除队列

//自动生成队列名
//非持久,独占,自动删除
String queueName = ch.queueDeclare().getQueue();

1.3.2 绑定 Bindings

在这里插入图片描述
我们已经创建了一个fanout交换机和一个队列。现在我们需要告诉exchange向指定队列发送消息。exchange和队列之间的关系称为绑定。

//指定的队列,与指定的交换机关联起来
//成为绑定 -- binding
//第三个参数时 routingKey, 由于是fanout交换机, 这里忽略 routingKey
ch.queueBind(queueName, "logs", "");

现在, logs交换机将会向我们指定的队列添加消息

列出绑定关系:

rabbitmqctl list_bindings

1.3.3代码展示

生产者

生产者发出日志消息,看起来与前一教程没有太大不同。最重要的更改是,我们现在希望将消息发布到logs交换机,而不是无名的日志交换机。我们需要在发送时提供一个routingKey,但是对于fanout交换机类型,该值会被忽略。

package m3_publishsubscribe;

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

import java.util.Scanner;

public class Producer {
    public static void main(String[] args) throws Exception {
        //1.连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c  = f.newConnection().createChannel();

        //2.定义交换机,两种写法
        //c.exchangeDeclare("logs","fanout");
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);

        //向交换机发送数据
        while (true){
            System.out.println("输入消息:");
            String msg = new Scanner(System.in).nextLine();
            //第二个参数,对与fanout交换机无效
            c.basicPublish("logs","",null,msg.getBytes());
        }

    }
}

启动查看一下代码有没有成功!访问一下rabbitmq(要确保rabbitmq虚拟机处于运行状态)
这是我的Linux虚拟机的IP地址,自己查看一下你的虚拟机的ip:http://192.168.126.129:15672
在这里插入图片描述

此时,还没有建立消费者,那么数据都哪去了呢?

答:没有接收者,数据就消失了!!!

消费者

如果还没有队列绑定到交换器,消息就会丢失,但这对我们来说没有问题;如果还没有消费者在听,我们可以安全地丢弃这些信息。

package m3_publishsubscribe;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.UUID;

public class Consumer {
    public static void main(String[] args) throws Exception {
        //一、连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.126.129");//写rabbitmq服务器地址
        f.setPort(5672);//5672是通信端口,收发消息是5672,15672是管理界面
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c  = f.newConnection().createChannel();

        /**
         * 二、先做三步:
         *  1、定义交换机
         *  2、定义随机队列
         *  3、绑定到交换机
         */
        //定义交换机
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);

        //随机定义队列名方法 ①
        //String queue = UUID.randomUUID().toString();//随机定义队列名
        //            //随机队列名   非持久     独占     自动删除  其他参数
        //c.queueDeclare(queue,false,true,true,null);

        //随机定义队列名方法 ②
        // 让rabbitmq服务器来随机命名,非持久,独占,自动删除,
        String queue = c.queueDeclare().getQueue();

        //绑定交换机
        //第三个参数,对fanout交换机无效
        c.queueBind(queue,"logs","");

        //四、创建匿名内部类
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String consumerTag, Delivery message) throws Exception {
                //将消息还原成字符串
                String msg = new String(message.getBody());
                System.out.println("收到: "+msg);
            }
        };
        CancelCallback cancelCallback = new CancelCallback() {
            @Override
            public void handle(String consumerTag) throws IOException {
            }
        };

        //三、正常消费数据
        c.basicConsume(queue, true, deliverCallback, cancelCallback);
    }
}

消费者启动需要启动多个!!

设置好之后多次启动
在这里插入图片描述
当启动多个消费者的时候,还可以进行反向查看。

  1. 展开bindings
  2. 点击选中的②
  3. 可以查看到如下图所示
    在这里插入图片描述
    在这里插入图片描述

生产者、消费者都定义同一个交换机的原因??

答:谁先启动谁负责把交换机创建出来,也就是和启动顺序有关!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

经理,天台风好大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值