RabbitMQ基本消息模型

240 阅读4分钟

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

RabbitMQ基本消息模型

我在本地虚拟机已经搭建好了RabbitMQ,并建立了用户。

RabbitMQ是一个消息代理接收消息和转发消息,可以看作是一个快递员,有人把邮寄的东西给快递员,你可以确定快递员最后会把东西送到你指定的人手上,RabbitMQ时取件员,快递公司,派件员。

image.png

P(producer/ publisher):生产者,一个发送消息的用户应用程序。

C(consumer):消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序

队列(红色区域):rabbitmq内部类似于邮箱的一个概念。虽然消息流经rabbitmq和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。

我们通过java代码编写生产者和消费者,生产者连接到Rabbitmq发送消息然后退出

public class Producer_HelloWorld {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //2.设置参数
        connectionFactory.setHost("192.168.145.3");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/zhaojin");
        connectionFactory.setUsername("zhaojin");
        connectionFactory.setPassword("zhaojin");
        //3.创建连接
        Connection connection = connectionFactory.newConnection();
        //4.创建 channel
        Channel channel = connection.createChannel();
        //5.创建queue
        /*
        *   `1.queue队列名称
        *    2.durable 是否持久化
        *    3.exclusive 是否独占,只有一个消费者监听队列2是否关闭时删除队列
        *    4.outdelete 是否自动删除
        *    5.argments 参数信息
        * */
        channel.queueDeclare("hello_world",true,false,false,null);
        //6.发送消息
        /*
        * 1.exchange 交换机名称,简单模式使用默认的
        * 2.routingkey 路由名称
        * 3.props 配置信息
        * 4,。bady 数据信息
        * */

        String bady ="hello RabbitMQ";
        channel.basicPublish("","hello_world",null,bady.getBytes());

        //7.释放资源
        channel.close();
        connection.close();
    }
}

控制台没有报错

image.png 再看一下RabbitMQ,有一个 hello world的队列一条消息

image.png 再运行一遍,在看控制台有两条未被消费的消息

image.png 现在我们用消费者连上mq来消费者来消费这两条消息

public class Consumer_HelloWorld {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //2.设置参数
        connectionFactory.setHost("192.168.145.3");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/zhaojin");
        connectionFactory.setUsername("zhaojin");
        connectionFactory.setPassword("zhaojin");
        //3.创建连接
        Connection connection = connectionFactory.newConnection();
        //4.创建 channel
        Channel channel = connection.createChannel();
        channel.queueDeclare("hello_world",true,false,false,null);
        //6.接受消息
        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumerTag:"+consumerTag);
                System.out.println("exchange:"+envelope.getExchange());
                System.out.println("routingkey:"+envelope.getRoutingKey());
                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                System.out.println("consumerTag:"+consumerTag);
            }
        };
        channel.basicConsume("hello_world",true,consumer);
    }
}

控制台输出

image.png 刚才的两条消息都被消费掉了,再看RabbitMq控制台,刚才的消息都被消费掉了,变成了0,但是消费者没有停止运行,一直监听着队列,一旦队列有了新消息,就会立即消费。

image.png

RabbitMQ怎么知道消息有没有被消呢?

我们再运行一遍生产者你会发现,我们发送的消息几乎瞬间被消费者消费掉,在控制台基本看不到消息停留,那如果消费者休闲异常死掉了,拿到消息有没有消费成功我们就不不知道了,这样我们的目的就没有达到,因此QabbitMq有他一个ACk机制,消费者获取到消息后,会给RabbitMQ发送回执消息,告知消息已被接受,有两种情况:

  • 自动ACK:消息一旦被接收,消费者自动发送ACK
  • 手动ACK:消息接收后,不会发送ACK,需要手动调用

之前都是自动ACK, 那自动ack存在什么问题?

生产者不做任何修改,发送一条消息,发送成功

image.png 我们在消费者制造异常

image.png

运行消费者,并没有打印之前消费的信息,

image.png 控制台消息被消费了

image.png

手动ACK只需要修改下列方法的第二个参数,如果第二个参数为true,则会自动进行ACK;如果为false,则需要手动ACK。

 public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {
        return this.basicConsume(queue, autoAck, "", callback);
    }

修改完成

 channel.basicConsume("hello_world",false,consumer);

运行生产者发送消息

image.png 运行消费者接收消息

image.png 查看RabbitMq控制台,因为我们设置了手动ack,但是代码里没有返回,消息没有被真正消费掉,变成unacked没有收到ack。

image.png 现在我们关掉消费者,消息有重新回到Ready状态了

image.png

修改消费者手动ACK确认,消费成功。

image.png

image.png