中间件——消息队列之RabbitMQ

概念

主流的消息队列之一,遵循AMQP协议,由内在高并发的Erlang语言开发,用在实时的对可靠性要求比较高的消息传递上。

消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。

消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。

 

优势

安装部署简单

企业级消息队列经过大量实践考验可靠性

有强大的WEB管理页面

功能丰富

 

角色

  • Message
    消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
  • Publisher
    消息的生产者,也是一个向交换器发布消息的客户端应用程序。
  • Exchange
    交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
  • Binding
    绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
  • Queue
    消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
  • Connection
    网络连接,比如一个TCP连接。
  • Channel
    信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
  • Consumer
    消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
  • Virtual Host
    虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 "/ "。当在RabbitMQ中创建一个用户时,用户通常会被指派给至少一个vhost,并且只能访问被指派vhost内的队列、交换器和绑定,vhost之间是绝对隔离的。
  • Broker
    表示消息队列服务器实体。

 RabbitMQ原理图

               RabbitMQ原理-图

               路由模式-图

                通配符模式-图

                订阅模式-图

RabbitMQ使用小案例

RabbitMQ 支持多种语言访问,以 Java 为例看下一般使用 RabbitMQ 的步骤:

先提前安装好RabbitMQ服务,Linux,Windows下的版本都行

1.添加依赖

<dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>3.4.1</version>
</dependency>

2.编写一个连接类获取MQ的连接:ConnectionUtil

package com.example.springboot_rabbitmq.util;

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

public class ConnectionUtil {

    public static Connection getConnection() throws Exception {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("localhost");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 通过工程获取连接
        Connection connection = factory.newConnection();
        if (connection.isOpen()){
            System.out.println("连接成功");
        }else{
            System.out.println("连接失败");
        }
        return connection;
    }

    public static void main(String[] args) {
        //测试连接
        try {
            System.out.println(ConnectionUtil.getConnection());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

3.开启RabbitMQ服务,运行main方法,测试连接成功!!!

 

4.使用浏览器访问:http://localhost:15672

5.生产者发送消息到队列

package com.example.springboot_rabbitmq.simple;


import com.example.springboot_rabbitmq.util.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class Send {

    private final static String QUEUE_NAME = "q_test_01";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        // 从连接中创建通道
        Channel channel = connection.createChannel();

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

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");
        //关闭通道和连接
        channel.close();
        connection.close();
    }

}

6.管理工具中查看消息

 

 

 

 消息在这里打印出来了!!!

 

 

 7.消费者从队列中获取消息

package com.example.springboot_rabbitmq.simple;

import com.example.springboot_rabbitmq.util.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

public class Recv {
    private final static String QUEUE_NAME = "q_test_01";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        // 从连接中创建通道
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

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

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
        }
    }

}

8.信息获取成功!!!

 

消息丢失怎么办?

解决:消息持久化

  • 交换机持久化
  • 队列持久化
  • 消息持久化

消费者受到消息处理后自身业务发生异常如何处理?

解决:将消息加入死信队列

  • 创建死信交换机
  • 创建死信队列
  • 将正常的队列和死信队列以及死信交换机进行绑定
posted @ 2021-12-17 20:34  Sakimir  阅读(23)  评论(0)    收藏  举报