RabbitMQ常见的消息模型

一、RabbitMQ

1、MQ介绍

       MQ(Message Queue):消息队列又叫消息中间件。业务系统里面可通过典型的生产者和消费者模型实现系统A和系统B之间的解耦。因为消息的生产和消费都是异步的,而且双方只关心消息的发送和接收,中间没有业务逻辑的侵入,可以轻松实现系统间的解耦。


2、不同MQ的特点

① ActiveMQ
       ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。它是一个完全支持JMS规范的的消息中间件。丰富的API,多种集群架构模式让ActiveMQ在业界成为老牌的消息中间件,在中小型企业颇受欢迎!但是数据量一旦达到某个量级,性能就会很差

② Kafka
       Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务(进行大数据的分析)

③ RocketMQ
       RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

④ RabbitMQ
       RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。


3、安装前置依赖Erlang

先运行下面的指令为erlang设置好仓库。

	curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash

	# 安装erlang
	yum install erlang

Alt

出现了上面的错误,不急,先使用下面的指令安装完deltarpm之后,再执行yum install erlang

	yum provides '*/applydeltarpm'    #查看依赖包的位置
	yum -y  install deltarpm             #安装命令

如下图则erlang安装成功。
Alt


可以使用下面指令查看erlang是否成功安装。

	erl

Alt
Alt

使用下面指令安装socat。

	yum install socat

4、安装RabbitMQ

安装RabbitMQ。

	rpm -ivh rabbitmq-server-3.8.5-1.el7.noarch.rpm 

如下图则表示RabbitMQ安装成功。

Alt


注意:高版本的RabbitMQ不带配置文件,需要去github上面拷贝并放到/etc/rabbitmq目录下面。

rabbitmq-server-3.8.5-1.el7.noarch.rpm包下载链接,提取码:o9z3

rabbitmq的配置文件样例下载链接,提取码:q4tu


5、RabbitMQ常用指令

① 启动rabbitmq中的插件管理。

	rabbitmq-plugins enable rabbitmq_management

出现下图则表示启动成功。
Alt

② RabbitMQ服务的启动、重启与停止。

	# 启动RabbitMQ服务
	systemctl start rabbitmq-server		
	
	# 重启RabbitMQ服务
	systemctl restart rabbitmq-server		

	# 停止RabbitMQ服务
	systemctl stop rabbitmq-server		

③ 查看RabbitMQ服务的状态。

	systemctl status rabbitmq-server

下图表示RbbitMQ处于运行状态。

Alt


6、管理界面测试

启用下面配置可以允许所有用户访问,而不仅仅是本地访问。

Alt


开启rabbitmq服务。

	service rabbitmq-server start 

开启web管理接口。

	rabbitmq-plugins enable rabbitmq_management

使用IP:15672

Alt




二、RabbitMQ的消息模型

       RabbitMQ中生产者和消费者机制是这样的:生产者负责生产消息并将该消息发送到交换机(需指定路由key),消费者根据路由key从交换机上找到对应的消息队列,从这个队列中取消息。

       注意:生产端和消费端都可以申明交换机或者队列,只要所申明的属性不变,RabbitMQ自己回去判断是否一存在过,从而保证交换机或者队列只创建一次。

Alt


1、Hello World消息模型

       这个消息模型不需要用到交换机,所以生产者在投递消息的时候,将路由key直接指定为消费者要消费的队列的名字即可。
Alt

       生产者代码:

	public class Producer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        String queueName = "hello world_queue";
	        //声明通道对应的消息队列(表示创建队列, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示队列名,
	        //第二个参数表示队列是否开启持久化
	        //第三个参数表示队列是否开启独占模式(独占表示一个队列只能有一个channel监听)
	        //第四个参数表示是否自动删除队列
	        //第五个参数表示额外附加参数
	        channel.queueDeclare(queueName, false, false, false, null);
	
	        String msg = "Hello, I am rabbitMQ!";
	        //第一个参数表示交换机名字
	        //第二个参数表示路由key(当第一个参数为空字符串即交换机为default时, 这个参数直接指向队列名称)
	        //第三个参数表示传递消息的额外设置
	        //第四个参数表示消息的具体内容
	        channel.basicPublish("", queueName, null, msg.getBytes());
	
	        channel.close();
	        connection.close();
	    }
	}

       消费者代码:

	public class Consumer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        String queueName = "hello world_queue";
	        //声明通道对应的消息队列(表示创建队列, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示队列名,
	        //第二个参数表示队列是否开启持久化
	        //第三个参数表示队列是否开启独占模式(独占表示一个队列只能有一个channel监听)
	        //第四个参数表示是否自动删除队列
	        //第五个参数表示额外附加参数
	        channel.queueDeclare(queueName, false, false, false, null);
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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、Work Queues消息模型

       这个模型和Hello World消息模型本质是一样的,只不过是队列上面多了几个消费者,这些消费者共同消费队列中的这些消息

Alt

       生产者代码:

	public class Producer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        String queueName = "work_queues";
	        //声明通道对应的消息队列(表示创建队列, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示队列名,
	        //第二个参数表示队列是否开启持久化
	        //第三个参数表示队列是否开启独占模式(独占表示一个队列只能有一个channel监听)
	        //第四个参数表示是否自动删除队列
	        //第五个参数表示额外附加参数
	        channel.queueDeclare(queueName, false, false, false, null);
	
	        String msg = "Hello, I am rabbitMQ!";
	        //第一个参数表示交换机名字
	        //第二个参数表示路由key(当第一个参数为空字符串即交换机为default时, 这个参数直接指向队列名称)
	        //第三个参数表示传递消息的额外设置
	        //第四个参数表示消息的具体内容
	        for (int i = 0; i < 20; i++) {
	            channel.basicPublish("", queueName, null, msg.getBytes());
	        }
	
	        channel.close();
	        connection.close();
	    }
	}

       消费者1代码:

	public class Consumer1 {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        String queueName = "work_queues";
	        //声明通道对应的消息队列(表示创建队列, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示队列名,
	        //第二个参数表示队列是否开启持久化
	        //第三个参数表示队列是否开启独占模式(独占表示一个队列只能有一个channel监听)
	        //第四个参数表示是否自动删除队列
	        //第五个参数表示额外附加参数
	        channel.queueDeclare(queueName, false, false, false, null);
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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));
	            }
	        });
	    }
	}

       消费者2代码:

	public class Consumer2 {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        String queueName = "work_queues";
	        //声明通道对应的消息队列(表示创建队列, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示队列名,
	        //第二个参数表示队列是否开启持久化
	        //第三个参数表示队列是否开启独占模式(独占表示一个队列只能有一个channel监听)
	        //第四个参数表示是否自动删除队列
	        //第五个参数表示额外附加参数
	        channel.queueDeclare(queueName, false, false, false, null);
	
	        //消费端限流, 一次只接收一条未确认的消息
	        channel.basicQos(1);
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
	            @Override
	            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
	                try {
	                    TimeUnit.SECONDS.sleep(1);
	                } catch (InterruptedException e) {
	                    e.printStackTrace();
	                }
	                //第一个参数是消息的唯一标识符, 第二份参数表示是否开启批量签收
	                channel.basicAck(envelope.getDeliveryTag(), false);
	                System.out.println("消费者2消费了一条消息: " + new String(body));
	            }
	        });
	    }
	}

3、Publich/Subscribe消息模型

       这个消息模型类似广播模式,生产者将消息投递给交换机,交换机不需要指定路由key,每个消费者有自己的队列,交换机会把消息发送给所有绑定该交换机的队列,只要消费者自己的消息队列绑定的是同一个交换机,就可以实现一条消息被多个消费者消费。交换机类型需要指定为fanout

Alt

       生产者代码:

	public class Producer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	
	        channel.exchangeDeclare("publish subscribe_exchange", "fanout");
	
	        String msg = "Hello, I am rabbitMQ!";
	        //第一个参数表示交换机名字
	        //第二个参数表示路由key(当第一个参数为空字符串即交换机为default时, 这个参数直接指向队列名称)
	        //第三个参数表示传递消息的额外设置
	        //第四个参数表示消息的具体内容
	        channel.basicPublish("publish subscribe_exchange", "", null, msg.getBytes());
	
	        channel.close();
	        connection.close();
	    }
	}

       消费者1代码:

	public class Consumer1 {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //创建临时队列, 每个消费者有自己的队列
	        String queueName = channel.queueDeclare().getQueue();
	
	        //声明交换机(表示创建交换机, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示交换机的名字
	        //第二个参数表示交换机的类型
	        channel.exchangeDeclare("publish subscribe_exchange", "fanout");
	        //绑定队列和交换机, fanout即发布订阅模式不需要路由key
	        channel.queueBind(queueName, "publish subscribe_exchange", "");
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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));
	            }
	        });
	    }
	}

       消费者2代码:

	public class Consumer2 {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //创建临时队列, 每个消费者有自己的队列
	        String queueName = channel.queueDeclare().getQueue();
	
	        //声明交换机(表示创建交换机, 不管是消费者那声明还是生产者那声明都可以)
	        //第一个参数表示交换机的名字
	        //第二个参数表示交换机的类型
	        channel.exchangeDeclare("publish subscribe_exchange", "fanout");
	        //fanout即发布订阅模式不需要路由key
	        channel.queueBind(queueName, "publish subscribe_exchange", "");
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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、Routing消息模型

       生产者将消息投递到交换机同时指定路由key,消费者根据路由key从对应的消息队列中获取消息进行消费,这个模型必须当双方的路由key相同时才能匹配到。
Alt

       生产者代码:

	public class Producer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //声明交换机
	        channel.exchangeDeclare("routing_exchange", "direct");
	
	        String msg = "Hello, I am rabbitMQ!";
	        //第一个参数表示交换机名字
	        //第二个参数表示路由key(当第一个参数为空字符串即交换机为default时, 这个参数直接指向队列名称)
	        //第三个参数表示传递消息的额外设置
	        //第四个参数表示消息的具体内容
	        channel.basicPublish("routing_exchange", "hello routing", null, msg.getBytes());
	
	        channel.close();
	        connection.close();
	    }
	}

       消费者代码:

	public class Consumer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //创建临时队列
	        String queueName = channel.queueDeclare().getQueue();
	
	        //绑定队列和交换机
	        channel.queueBind(queueName, "routing_exchange", "hello routing");
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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));
	            }
	        });
	    }
	}

5、Topics消息模型

       这个消息模型其实就是Routing消息模型的升级版,消费者的路由key可以使用#表示匹配大于等于零个词*表示匹配一个词,其他都和第四种消息模型一样。
Alt

       生产者代码:

	public class Producer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //声明交换机
	        channel.exchangeDeclare("topics_exchange", "topic");
	
	        String msg = "Hello, I am rabbitMQ!";
	        //第一个参数表示交换机名字
	        //第二个参数表示路由key(当第一个参数为空字符串即交换机为default时, 这个参数直接指向队列名称)
	        //第三个参数表示传递消息的额外设置
	        //第四个参数表示消息的具体内容
	        channel.basicPublish("topics_exchange", "user", null, msg.getBytes());
	
	        channel.close();
	        connection.close();
	    }
	}

       消费者代码:

	public class Consumer {
	    public static void main(String[] args) throws Exception {
	        Connection connection = RabbitMQUtils.getConnection();
	        Channel channel = connection.createChannel();
	
	        //创建临时队列
	        String queueName = channel.queueDeclare().getQueue();
	
	        //绑定队列和交换机, #表示匹配零个、一个或多个词, *表示只匹配一个词
	        channel.queueBind(queueName, "topics_exchange", "user.#");
	
	        //创建消费者
	        //第一个参数表示队列名
	        //第二个参数表示是否开启自动签收消息
	        //第三个消息是消费时候的回调接口
	        channel.basicConsume(queueName, 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));
	            }
	        });
	    }
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值