初学RabbitMQ

1 MQ引言

1.1 什么是MQ

MQ(Message Quene):翻译为消息队列,通过典型的生产者,消费者模型,生产者不断向消息队列中生产消息,消费者不断从消息队列中获取消息。因为消费者和生产者都是异步的,而且只关心消息的生产和接收,没有业务逻辑的侵入,轻松实现系统之间的解耦。别名为消息中间件 通过利用平台高效可靠的消息传递机制和平台无关的数据交流,并基于数据通信来进行分布式系统集成。

1.2 MQ有哪些

有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ,炙手可热的kafka

1.3 不同MQ的特点

在这里插入图片描述

2 RabbitMQ的引言

2.1 RabbitMQ

基于AMQP协议,erlang语言开发,是部署最广泛的消息中间件之一,是最受欢迎消息中间件之一。

对于消息的错误、丢失的处理都非常的友好

消息的都是,可以通过内部的机制对消息进行一个恢复

能够很好的与spring进行一个集成

在这里插入图片描述

2.2 RabbitMQ的安装

在这里插入图片描述

2.3 一共需要三个包

erlang-22.0.7-1.el7.x86_64.rpm  
rabbitmq-server-3.7.18-1.el7.noarch.rpm  
socat-1.7.3.2-2.el7.x86_64.rpm

2.4 rpm命令安装

#安装Erlange 依赖包
rpm -ivh erlang-22.0.7-1.el7.x86_64.rpm 

rpm -ivh socat-1.7.3.2-2.el7.x86_64.rpm
#安装RabbitMQ安装包,需要联网
rpm -ivh rabbitmq-server-3.7.18-1.el7.noarch.rpm

#查找Rabbit MQ配置文件的位置
find / -name rabbitmq.config.example
/usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example


#复制配置文件
cp /usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config

#查看配置文件的位置
/etc/rabbitmq/rabbitmq.config
#修改配置文件参数,打开来宾账户,在配置文件的61行
{loopback_users, []}

#启动管理插件
rabbitmq-plugins enable rabbitmq_management

#启动RabbitMQ
systemctl start rabbitmq-server

#重启RabbitMQ服务
systemctl restart rabbitmq-server

#终止RabbitMQ服务
systemctl stop rabbitme-server

#查看服务状态
systemctl status rabbitmq-server

#RabbitMQ端口号
15672

#访问RabbitMQ
ip:15672

#来宾账户密码
账号:guest
密码:guest


3 RabbitMQ配置

3.1 RabbitMQ命令行管理

#服务启动查看结束
systemctl start|restart|stop|status rabbitmq-server

#命令管理,在不使用web界面来管理RabbitMQ的时候使用
rabbitmqctl help   查看更多命令

#插件管理命令
rabbitmqplugins enable|list|disable 

3.2 WEB管理界面

3.2.1 Overview

概览

3.2.2 Connections

链接

客户端当前的链接情况

3.2.3 Channels

通道

消息队列通过通道来进行传递消息

3.2.4 Exchanges

交换机(路由)

RabbitMQ安装之后含有内置的七个路由规则

若内置的路由规则,对需求不可用,可以自己配置路由规则

3.2.5 Queues

队列

用来绑定交换机(路由)

可以自定义管理队列

3.2.6 Admin

管理

对用户来进行管理,可以对用户进行正则匹配

管理现有用户

添加新的用户,分配不同的权限

创建虚拟主机,为用户进行绑定

用户只能访问绑定的虚拟主机

3.3 RabbitMQ

在这里插入图片描述

使用时,先创建用户,在创建虚拟主机,在将虚拟主机绑定到用户上面

生产者与RabbitMQ server 进行连接,讲消息通过通道来传递消息,将消息发送给交换机,通过交换机将消息发送给消费者,每个通道对应一个虚拟主机(可以看成一个库),可以使多个虚拟主机互不影响,虚拟主机需要权限,虚拟主机需要和用户绑定在一起;

4 RabbitMQ的第一个程序

4.1RabbitMQ的七种消息机制

4.1.1 Hello World(直连)

"Hello World!"

4.1.2 Work queues (工作队列)

Work queues

4.1.3 Publish/Subscribe(发布/订阅)

Publish/Subscribe

4.1.4 Routing(路由 direct 直连)

Routing

4.1.5 Topics (路由 topic 通配)

Topics

4.1.6 RPC(请求回复模式)

RPC

4.1.7 Publisher Confirms (发布确认)

4.2 工具

4.2.1 引入依赖
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.7.2</version>
</dependency>
4.2.2 封装连接工厂
 private static final ConnectionFactory connectionFactory;
    static {
        connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("xin");
        connectionFactory.setUsername("xin");
        connectionFactory.setPassword("xin");
    }
    //定义提供连接对象的方法
    public static Connection getConnection(){
        try {
            return connectionFactory.newConnection();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }
    //关闭连接通道的方法
    public static void closeConnectionAndChanel(Channel channel, Connection connection){
        try {
            if (channel != null) channel.close();
            if (connection != null) connection.close();
        } catch (IOException |TimeoutException e) {
            e.printStackTrace();
        }
    }

4.3 第一种模型(直连)

在这里插入图片描述

  • P:生产者,也就是要发送消息的程序
  • C:消费者,消息的接受者,一直会等到消息的来到
  • queue:消息队列,图中红色部分,类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从中取出来。
4.3.1 开发生产者
4.3.1.1 创建库和用户

登录图形化操作界面:

创建虚拟主机

创建用户

为用户分配权限,分配空间

4.3.1.2 代码编写
 //消息生产者
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
        //创建连接mq的连接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接RabbitMQ连接主机
        connectionFactory.setHost("localhost");
        //设置端口号
        connectionFactory.setPort(5672);
        //设置虚拟主机
        connectionFactory.setVirtualHost("xin");
        //设置连接用户名 密码
        connectionFactory.setUsername("xin");
        connectionFactory.setPassword("xin");
        //获取连接对象
        Connection connection = connectionFactory.newConnection();
        //获取连接中通道
        Channel channel = connection.createChannel();
        //绑定消息队列
            //参数一:队列名称,如果队列中不存在这个名称,则自动创建一个队名称
            //参数二:;用来定义队列是否要持久化,如果为 true  则为持久化,如果为false 则不持久化
            //参数三:exclusive 独占队列  true 独占队列  false  不独占
            //参数四:autoDelete 是否在消费完成之后删除队列  true  删除队列  false  不删除队列
            //参数五:额外附加参数
        channel.queueDeclare("hello",false,false,false,null);
        // 发布消息
            //参数1:交换机名称
            //参数2:队列名称
            //参数3:传递消息额外设置
            //参数4:消息具体内容
        channel.basicPublish("","hello",null,"hello RabbitMQ".getBytes());
        //关闭通道
        channel.close();
        //关闭连接
        connection.close();

    }
4.3.2 开发消费者
public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //主机
        connectionFactory.setHost("localhost");
        //端口
        connectionFactory.setPort(5672);
        //虚拟主机
        connectionFactory.setVirtualHost("xin");
        //用户名
        connectionFactory.setUsername("xin");
        //密码
        connectionFactory.setPassword("xin");
        //创建连接对象
        Connection connection = connectionFactory.newConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //通道绑定对象
        channel.queueDeclare("hello",false,false,false,null);
        //消费者消费消息
        channel.basicConsume("hello",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者消费的消息"+new String(body));
            }
        });
    }

4.4 API参数细节

保证生产者和消费者队列的参数一致

4.4.1 队列持久化
        //参数一:队列名称,如果队列中不存在这个名称,则自动创建一个队名称
        //参数二:;用来定义队列是否要持久化,如果为 true  则为持久化,如果为false 则不持久化   持久化指的是:如果RabbitMQ的的服务重启或者中断的话,原有的消息队列没有做持久化处理,那么原有的消息队列就会丢失,如果做了持久化处理,那么RabbitMQ服务重启,消息是被储存在了磁盘中,就不会丢失了;
        //参数三:exclusive 独占队列  true 独占队列  false  不独占
        //参数四:autoDelete 是否在消费完成之后删除队列  true  删除队列  false  不删除队列
        //参数五:额外附加参数
        channel.queueDeclare("hello",false,false,false,null);

        //队列持久化
        channel.queueDeclare("hello",true,false,false,null);
		//自动删除队列,即消费者消费过这个队列,并且和这个队列断开,那么这个队列就会被自动删除。使用过程中,生产者和消费者的参数列表要一一对应。
		 channel.queueDeclare("hello",true,false,true,null);


4.4.2 消息持久化
        // 发布消息
        //参数1:交换机名称
        //参数2:队列名称
        //参数3:传递消息额外设置
        //参数4:消息具体内容
        channel.basicPublish("","hello",null,"hello RabbitMQ".getBytes());
        //消息持久化
        channel.basicPublish("","hello",MessageProperties.PERSISTENT_TEXT_PLAIN,"hello RabbitMQ".getBytes());

4.4第二种消息模型(work quene)

work quene 也被称为 Task quenes,任务模型。当消息处理比较耗时 的时候,即生产消息的速度远远大于消费消息的速度,长此以往,消息就会积累的越来越多,无法及时的处理。此时就可以使用work模型:让多个消费者绑定到一个队列中,共同消费队列中的消息,队列中的消息一旦被消费,就会消失,因此任务是不会被重复执行的。

在这里插入图片描述

角色:

  • P:生产者:任务的发布者
  • C1:消费者1,领取任务并且完成任务,假设消费消息的速度较慢
  • C2:消费者2,领取任务并完成任务,假设消费消息的速度较快
4.4.1 创建消费者
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("aa",true,false,false,null);
        for (int i = 0; i < 50; i++) {
            channel.basicPublish("","aa",null,(i + "hello RabbitMQ").getBytes());
        }
        RabbitMQDBUtil.closeConnectionAndChanel(channel,connection);
    }
4.4.2 创建消费者
4.4.2.1 消费者一
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare("aa",true,false,false,null);
        channel.basicConsume("aa",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者=== 1 === 消费"+new String(body));
            }
        });
    }
4.4.2.2 消费者2
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare("aa",true,false,false,null);
        channel.basicConsume("aa",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者=== 2 === 消费"+new String(body));
            }
        });
    }
4.4.3 结论

RabbitMQ按顺序将消息分发给每个消费者,平均而言,每个消费者接受的消息的数量是相同的,这种分发消息的机制称为循环

4.4.4 消息自动确认机制

能者多劳

public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("aa",true,false,false,null);
        // 一次只确认一条消息
        channel.basicQos(1);
        //参数2  关闭自动确认消息
        channel.basicConsume("aa",false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者=== 1 === 消费"+new String(body));
                //手动确认消息
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
  • 设置通道一次只能消费一个消息
  • 关闭消息自动确认
  • 开启手动确认消息

4.5第三种消息模型(fanout)

fanout扇出,也称之为广播

在这里插入图片描述

在广播模式下,消息发送流程是这样的:

  • 可以有多个消费者
  • 每个消费者都有自己的queue(队列)
  • 每个队列都要绑定到exchange(交换机)
  • 生产者发送消息只能发送到交换机,由交换机决定将消息发送到那个队列,生产者无法决定
  • 交换机将消息发送给绑定过的所有的队列
  • 队列的消费者都能拿到消息,实现一条消息被多个消费者进行消费
4.5.1 开发生产者
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();

        //声明exchange(交换机)
        channel.exchangeDeclare("logs","fanout");//广播,一条消息可以供多个消费者使用
        //发布消息
        channel.basicPublish("logs","",null,"hello RabbitMQ".getBytes());
    }
4.5.2 开发消费者 1 …

消费者有多个

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

        //绑定交换机
        channel.exchangeDeclare("logs","fanout");
        //创建临时队列,消费者停止获取消息,消息队列释放
        String queue = channel.queueDeclare().getQueue();
        //将临时队列绑定到exchange上面
        channel.queueBind(queue,"logs","");
        //处理消息
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者 一  获取的消息是:"+new String(body));
            }
        });
    }

4.6第四种消息模型(Routing)

4.6.1 Routing之订阅模式 direct 直连

在fanout模式中,一条消息,会被所有订阅的队列都消费。但是在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。

在Direct模型之下:

  • 队列与交换机进行绑定,不能进行任意的绑定了,而是要指定一个Routing Key(路由 Key)
  • 消息的发送在向Exchange发送消息时,也必须指定消息的Routing Key。
  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing key进行判断,只有对列的 Routing key 与消息的 Routing key一致的时候,才会收到消息。

在这里插入图片描述

图解:

  • P:生产者,向Exchange发送消息,发送消息的时会指定一个Routing Key
  • X:Exchange(交换机) 接受生产者生产的消息,然后把消息传递给Routing Key 完全匹配的队列
  • C1:消费者1,其所在队列指定了需要的Routing key 为 error 的消息
  • C2:消费者2,其所在队列指定了需要的routing key 为 info、error、warning的消息
4.6.1.1 开发生产者
    public static void main(String[] args) throws IOException {
            Connection connection = RabbitMQDBUtil.getConnection();
            // 获取连接通道
            Channel channel = connection.createChannel();
         	String logsDirect = "logs_direct";
            // 通过通道声明交换机
            // 参数一:交换机名称
            // 参数二:路由模式
            channel.exchangeDeclare(logsDirect,"direct");
            // 定义路由key 的名字
            String routingKey = "error";
            // 发布消息
            channel.basicPublish(logsDirect,routingKey,null,("指定的key"+routingKey+"发送的消息").getBytes());
            // 关闭资源
            RabbitMQDBUtil.closeConnectionAndChanel(channel,connection);
        }
4.6.1.2 开发消费者

消费者一

    public static void main(String[] args) throws IOException {
            Connection connection = RabbitMQDBUtil.getConnection();
            // 获取通道对象
            Channel channel = connection.createChannel();
            String logsDirect = "logs_direct";
            // 声明交换机以及交换机的类型
            channel.exchangeDeclare(logsDirect,"direct");
            //声明一个临时的路由
            String queue = channel.queueDeclare().getQueue();
            // 基于路由key去绑定对了和交换机
            channel.queueBind(queue,logsDirect,"error");
            //消费消息
            channel.basicConsume(queue,true,new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    System.out.println("消费者 一  来获取的消息"+new String(body));
                }
            });
        }

消费者二

public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        //获取消息队列通道
        Channel channel = connection.createChannel();
        String logsDirect = "logs_direct";
        //声明交换机以及交换机的类型
        channel.exchangeDeclare(logsDirect,"direct");
        //创建一个临时队列
        String queue = channel.queueDeclare().getQueue();
        //临时对了和交换机进行绑定
        channel.queueBind(queue,logsDirect,"info");
        channel.queueBind(queue,logsDirect,"error");
        channel.queueBind(queue,logsDirect,"warning");
        //获取消息
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者  二  获取的消息"+new String(body));
            }
        });
    }

4.7第五种消息模型(Topics)

Topic 类型的Exchangedirect 相比,都是可以根据 Routing key 把消息路由到不同的队列。只不过Topic类型的Exchange可以让队列绑定在Routing key 的时候通配,这种模式的Routing key 一般都是由一个或者多个单词组成,多个单词之间以“.”分割,例如“item.inseer”

在这里插入图片描述

// 通配符
	*(star) 匹配不多不少恰好一个单词;
	#(hash) 匹配一个或多个单词;
// 例如
    audit.# 匹配以audit.irs.corporate 或者 audit.irs
    aydit.* 只能匹配 aydit..irs
    
4.7.1 开发生产者
 public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        // 创建连接通道
        Channel channel = connection.createChannel();
        //连接交换机
        channel.exchangeDeclare("topics","topic");
        //发布消息
        String routKey = "save.user.sir";
        channel.basicPublish("topics",routKey,null,("这里是动态路由key["+routKey+"}").getBytes());
        RabbitMQDBUtil.closeConnectionAndChanel(channel,connection);

    }
4.7.2 开发消费者
// 消费者 一 
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机,以及交换机类型
        channel.exchangeDeclare("topics","topic");
        // 创建一个临时的队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机
        channel.queueBind(queue,"topics","save.*");
        // 消费消息
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("获取的消息队列"+new String(body));
            }
        });

    }

// 消费者 2 
public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQDBUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机,以及交换机类型
        channel.exchangeDeclare("topics","topic");
        // 创建一个临时的队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机
        channel.queueBind(queue,"topics","save.#");

        // 消费消息
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者2获取的消息队列"+new String(body));
            }
        });

    }

4.8第六种消息模型()

在这里插入图片描述

4.9第七种消息模型()

		//声明交换机,以及交换机类型
        channel.exchangeDeclare("topics","topic");
        // 创建一个临时的队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机
        channel.queueBind(queue,"topics","save.#");

        // 消费消息
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者2获取的消息队列"+new String(body));
            }
        });

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码不能跑我能跑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值