RabbitMQ基本概念和原理
-
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
-
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写。
-
Channel - Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。
-
Exchange(生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个或多个Queue中(或者丢弃),RabbitMQ常用的Exchange Type有fanout、direct、topic、headers这四种)
-
Queue(RabbitMQ的内部对象,用于存储消息)
-
基本工作原理
-
客户端(生产者) send Message -->Exchange (交换机通过不同的类型将消息保存到对应的queue)
–>Queue
-
客户端(消费者)通过订阅来消费queue中的消息。
-
安装说明
安装 RabbitMQ
Window下:
-
下载并安装《 otp_win64_21.0.1.exe 》
– Erlang程序运行环境 (MQ运行环境)
-
下载并安装《 rabbitmq-server-3.7.7.exe 》
– RabbitMQ服务器程序
-
启动RabbitMQ服务
-
激活RabbitMQ管理控制台(因为有一个操作面板所以需要激活)
-
打开RabbitMQ的安装位置 =》打开rabbitmq_server-3.7.7文件夹
=》找到sbin ==>》在绝对路径条中输入cmd =》进入后输入命令
– rabbitmq-plugins.bat enable rabbitmq_management
-
-
浏览器访问:http://localhost:15672
(Window系统默认自动运行MQ)
如果浏览器访问,没有直接打开:在sbin目录下双击service 不行就双击 server 手动启动服务
- 有两个端口:
- 15672:通过浏览器的方式操作MQ
- 5672:写代码时进行消息传递
– 管理员账户:guest
– 密 码:guest
- 有两个端口:
安装《 otp_win64_21.0.1.exe 》
( 如有弹窗 Error opening file for writing 点击忽略 )
双击打开 ==》 Next ==》 Next ==》 Install ==》 ⇧⇧⇧ ==》 完成点击 Close 关闭
安装《 rabbitmq-server-3.7.7.exe 》
双击打开 ==》 Next ==》 (选择安装位置) Install ==》 Next ==》 Finish
Linux下:
-
下载《 esl-erlang_21.0-1centos7_amd64.rpm 》
– Erlang运行环境rpm包
-
下载《 rabbitmq-server-3.7.7-1.el7.noarch.rpm 》
– rabbitmq服务器程序
-
通过XFTP将文件上传至temp目录
cd / 移动到根目录
mkdir upload 创建upload目录
cd upload/ 移动到upload目录下
- 传文件(两种方法)
1. rz 选择文件
2. 直接拖!!!(仅限在Xshell里)
-
rpm -ivh --nodeps esl-erlang_21.0-1~centos~7_amd64.rpm
– 安装此rpm包
-
rpm -ivh --nodeps rabbitmq-server-3.7.7-1.el7.noarch.rpm
– 安装此rpm包
-
rabbitmq-plugins enable rabbitmq_management
– 启用控制台
-
chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
– 授权
-
rabbitmq-server
– 启用服务
在CentOS里访问:localhost:15672(本机访问)
MQ-Linux常用命令
启动与关闭
rabbitmq-server
– 前台启用服务
rabbitmq-server -detached
– 后台启用服务(相比启动服务,后台启动多了 -detached 参数,
((不能使用ctrl+c) 后者防止卡顿,较为稳妥)
ps -aux | grep rabbitmq
– 看rabbitMQ是否启动成功
rabbitmqctl stop
– 停止服务(安全退出)
暂停与恢复
*- 做集群时需要用到
*- 让你的服务运行着,只不过给你暂停了方便做集群
*- 注:暂停与恢复不会对RabbitMQ的进程产生影响
rabbitmqctl stop_app
– 暂停
rabbitmqctl start_app
– 恢复
用户管理
<UserName:自定义用户名称,Password:自定义用户密码>
rabbitmqctl add_user UserName Password
– 创建用户(刚创建完成还不可直接登录需要授权角色)
rabbitmqctl delete_user UserName
– 删除用户
rabbitmqctl change_password UserName Password
– 修改密码
rabbitmqctl set_user_tags UserName Tag
– 授权角色 《四种 Tag: administrator、monitoring、policymaker、management》
rabbitmqctl set_permissions -p / UserName '.*' '.*' '.*'
– ’ .* ‘’ .* ‘’ .* ’ 这三个的意思分别为:1.配置权限 2.读权限 3.写权限 (不要怀疑就是写.*)
RabbitMQ用户四种Tag
超级管理员(administrator)
- 可登陆管理控制台(启用management plugin的情况下),可查看所有
的信息,并且可以对用户,策略(policy)进行操作。
监控者(monitoring)
- 登陆管理控制台(启用management plugin的情况下),同时可以查看
rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
策略制定者(policymaker)
- 可登陆管理控制台(启用management plugin的情况下)。同时可以对
policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
普通管理者(management)
- 仅可登陆管理控制台(启用management plugin的情况下),无法看到
节点信息,也无法对策略进行管理。
虚拟主机
– *-注:给角色赋予角色后 虚拟主机名称默认为:/
(” / “代表了一个数据库,相当于MySQL中的一个数据库,我们以后的数据将会放在这个虚拟主机下)
rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
虚拟主机名称可改
如何添加多个虚拟主机?(创建虚拟主机 / 所需用户添加虚拟主机)
- 如下图:☟
MQ通讯
AMQP
AMQP,即 Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 Rabbitmq等。
- AMQP是一种协议的标准,我们进行数据传输的时候必须要遵守这个底层的标准
基本概念
RabbitMQ六种工作模式
MQ 所需配置文件
<dependencies>
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<!-- 对象,转换成String类型 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
1.简单模式
-
Hello World - 又称simple简单模式
-
-
消息产生者§将消息放入队列
-
消息的消费者监听 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除
(隐患:消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失)
(应用场景:聊天( 中间有一个过度的服务器;p端,c端 )
-
-
生产者 | 消费者 | 点对点( 一生产 一消费 )
生产者
package com.mq.test;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* Hello World模式 && simple模式
*/
/*生产者*/
public class ProducerTest {
public static void main(String[] args) throws IOException, TimeoutException {
//创建工厂
ConnectionFactory cf = new ConnectionFactory();
//开始连接
cf.setHost("192.168.118.130"); //服务器的IP
cf.setPort(5672); //rabbitMQ的默认端口号
cf.setUsername("admin"); //用户名
cf.setPassword("123456"); //密码
cf.setVirtualHost("/test"); //虚拟主机的地址
//创建一个MQ的物理连接 ---- 这里也就获得到了和MQ的基于TCP的物理连接
Connection conn = cf.newConnection();
//创建通讯通道,相当于TCP中的虚拟连接,因为物理连接的开销是非常大的
/*建立连接(建立一个通道),只需要做一次TCP握手,反之每次都需要做TCP*/
//在物理连接上创建了多个虚拟连接减少物理连接的开销
Channel channel = conn.createChannel();
/*通过代码的方式创建队列,声明并创建一个队列,有则加载,无则创建*/
//第一个参数表示队列名称
//第二个参数表示是否持久化 false 否,mq停掉数据就会丢失 | true 是
//第三个参数表示是否私有化 false 表示所有的消费者都可以访问,true表示只有第一次拥有它的消费者才可以使用,其他消费者则不让访问
//第四个参数表示是否自动删除,false表示连接停掉后不自动删除这个队列 | true连接停掉后自动删除这个队列
//其他参数为:null
channel.queueDeclare("helloWorld",false,false,false,null);
//创建要发送的消息
String message = "helloWorld";//模拟传送到mq中的消息
/*进行数据的发送(向mq发送消息)*/
//第一个参数 exchange 交换机 暂时不用,后面进行发布订阅时才会用到
//第二个参数 routingKey 队列名称
//第三个参数 props 额外属性
//第四个参数 body 传递数据库的字节 --- 也就是传递信息
channel.basicPublish("","helloWorld",null,message.getBytes());
/*通常不关闭连接*/
//关闭虚拟连接
//channel.close();
//关闭物理连接
//conn.close();
System.out.println("数据发送成功!!!!!");
}
}
消费者
package com.mq.test;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* Hello World模式 && simple模式
*/
/*消费者*/
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//开始连接
factory.setHost("192.168.118.130"); //服务器的ip
factory.setPort(5672); //rabbitMQ的默认端口号
factory.setUsername("admin"); //用户名
factory.setPassword("123456"); //密码
factory.setVirtualHost("/test"); //虚拟主机的地址
//创建一个MQ的物理连接 --- 这里也就获得到了和MQ的基于TCP的物理连接
Connection conn = factory.newConnection();
//创建通道
Channel channel = conn.createChannel();
//绑定消息队列
channel.queueDeclare("helloWorld",false,false,false,null);
//创建一个消息消费者(消息的接收)
//第一个参数为队列名称
//第二个参数为是否自动确认收到消息,false代表手动编程来确认收到消息,这个是mq推荐做法
//第三个参数Consumer的实现类来手动的编程来确认消息的那个类是下面写的那个Recv对象
channel.basicConsume("helloWorld",false,new Recv(channel));//这里会对程序进行持续的监听只要有生产我就立马消费,不要进行关闭通道channel
/*不能关闭通道*/
//channel.close();
//conn.close();
}
}
/**
* Consumer的实现类
*/
class Recv extends DefaultConsumer {
private Channel channel;
//重写构造函数
//Channel通道对象需要从外层传入,在handleDelivery这个方法中要用到
public Recv(Channel channel) {
super(channel);
this.channel = channel; //接收外部传入的Channel对象
}
//这里的这个方法才是我们对消息进行处理的方法
//并且必须要重写
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body);//把字节数组的消息转换成String
System.out.println("接收到了消息:"+str);
//签收消息,也叫确认消息
//第一个参数envelope.getDeliveryTag(),获得这个消息的TagId,就是消息的唯一编号
//第二个参数 false表示只确认签收当前消息,如果为true表示签收该消费者所有未签收的消息
this.channel.basicAck(envelope.getDeliveryTag(), false);
}
}
封装工具类
package com.mq.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitMQUtils {
private static ConnectionFactory factory;
static {
factory = new ConnectionFactory();
factory.setHost("192.168.118.130"); //服务器的IP
factory.setPort(5672); //rabbitMQ的默认端口号
factory.setUsername("admin"); //用户名
factory.setPassword("123456"); //密码
factory.setVirtualHost("/test"); //虚拟主机的地址
}
/**
* 获得物理连接
* @return
*/
public static Connection getConnection() {
Connection connection = null;
try {
connection = factory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return connection;
}
}
------------------------------------------------------------------------------------
package com.mq.content;
/**
* 里面存放了队列名字的常量类
* 提高代码重用率
*/
public interface RabbitContent {
/**
* hello队列
*/
String QUEUE_HELLO="hello";
//public static final String QUEUE_HELLO = "hello";
}
《工具类写完后改动的地方》
package com.mq.test;
import com.mq.content.RabbitContent;
import com.mq.utils.RabbitMQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* Hello World模式 && simple模式
*/
/*生产者*/
public class ProducerTest {
public static void main(String[] args) throws IOException, TimeoutException {
//创建工厂
/*这里省略*/
//开始连接
/*这里省略*/
//创建一个MQ的物理连接 ---- 这里也就获得到了和MQ的基于TCP的物理连接
Connection conn = RabbitMQUtils.getConnection();/*改动位置*/
/*通过代码的方式创建队列,声明并创建一个队列,有则加载,无则创建*/
/*改动位置:队列名称修改*/
channel.queueDeclare(RabbitContent.QUEUE_HELLO,false,false,false,null);
/*进行数据的发送(向mq发送消息)*/
/*改动位置:队列名称*/
channel.basicPublish("",RabbitContent.QUEUE_HELLO,null,message.getBytes());
------------------------------------------------------------------------------------
/**
* Hello World模式 && simple模式
*/
/*消费者*/
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
//创建工厂
/*这里省略*/
//开始连接
/*这里省略*/
//创建一个MQ的物理连接 --- 这里也就获得到了和MQ的基于TCP的物理连接
Connection conn = RabbitMQUtils.getConnection();/*改动位置*/
//绑定消息队列
/*队列名称修改*/
channel.queueDeclare(RabbitContent.QUEUE_HELLO,false,false,false,null);
//创建一个消息消费者(消息的接收)
/*队列名称修改*/
channel.basicConsume(RabbitContent.QUEUE_HELLO,false,new Recv(channel));//这里会对程序进行持续的监听只要有生产我就立马消费,不要进行关闭通道channel
消费者 — 匿名内部类写法1
package com.mq.test;
import com.mq.content.RabbitContent;
import com.mq.utils.RabbitMQUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* Hello World模式 && simple模式
*/
/*消费者*/
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个MQ的物理连接 --- 这里也就获得到了和MQ的基于TCP的物理连接
Connection conn = RabbitMQUtils.getConnection();
//创建通道
final Channel channel = conn.createChannel();
//绑定消息队列
channel.queueDeclare(RabbitContent.QUEUE_HELLO, false, false, false, null);
//创建一个消息消费者(消息的接收)
//channel.basicConsume(RabbitContent.QUEUE_HELLO,false,new Recv(channel));//这里会对程序进行持续的监听只要有生产我就立马消费,不要进行关闭通道channel
channel.basicConsume(RabbitContent.QUEUE_HELLO, false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body);//把字节数组的消息转换成String
System.out.println("接收到了消息:"+str);
//签收消息,也叫确认消息
//第一个参数envelope.getDeliveryTag(),获得这个消息的TagId,就是消息的唯一编号
//第二个参数 false表示只确认签收当前消息,如果为true表示签收该消费者所有未签收的消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
/*不能关闭通道*/
//channel.close();
//conn.close();
}
}
2.工作队列
-
Work queues - 工作队列模式 (资源的竞争)
-
每个消费者获取到的消息都不一样
-
一个队列分发给不同的消费者
-
-
消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2,同时监听同一个队列,消息被消费?C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息
(隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用
//解决方法:可以设置一个开关(syncronize,与同步锁的性能不一样) 保证一条消息只能被一个消费者使用)
-
应用场景:红包;大项目中的资源调度(任务分配系统不需知道哪一个任务执行系统在空闲,直接将任务扔到消息队列中,空闲的系统自动争抢;12306给每个乘客发送消息:如果遇到性能较差的消费端半天收不到消息,也不影响其他消费者继续接收消息)(可以最大发挥消费端服务器性能)
-
-
3.发布订阅
- 发布(publish<生产者>)/订阅(subscribe<消费者>) - 发布订阅(共享资源)
- 每个消费者获取到的消息都是一样的 - 特别适合数据提供商与应用商
-
- X代表交换机rabbitMQ内部组件,erlang 消息产生者是代码完成,代码的执行效率不高,消息产生者将消息放入交换机,发布订阅交换机把消息发送到所有与之绑定消息队列中,对应消息队列的消费者拿到消息进行消费
- 交换机一个、消息队列多个、一个队列绑定一个消费者(用户)
- 当你关注某个生产者后吗,他有新的消息你立刻就会收到并且每个消息都是相同的
- 相关场景:邮件群发,群聊天,广播(广告)气象局推送气象信息
-
4.路由模式
- routing - 路由模式(相比发布订阅模式增加了筛选)
- 每个消费者接收到的消息可以不一样 < 但是需要精确匹配 > 例如:搜索北京;只输入北不可以,必须是精确匹配必须是“ = ”
-
-
消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;
-
根据业务功能定义路由字符串
-
从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中
业务场景:error 通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误;
-
-
5.主题模式
- topic - 主题模式(路由模式的一种)
- 相比较路由模式,主题模式可以进行模糊匹配 <例如:搜索北京;只输入北即可>
-
- 星号井号代表通配符
- 星号代表多个单词,井号代表一个单词
- 路由功能添加模糊匹配
- 消息产生者产生消息,把消息交给交换机
- 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费
6.远程模式
- RPC(不常用,不做解释)
- RPC远程调用模式
RabbitMQ 其他内容
http://www.cnblogs.com/zhangweizhong/category/855479.html
由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;
2. 根据业务功能定义路由字符串
3. 从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中
业务场景:error 通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误;
[外链图片转存中…(img-YSEg5S18-1645611005681)]
5.主题模式
- topic - 主题模式(路由模式的一种)
- 相比较路由模式,主题模式可以进行模糊匹配 <例如:搜索北京;只输入北即可>
-
- 星号井号代表通配符
- 星号代表多个单词,井号代表一个单词
- 路由功能添加模糊匹配
- 消息产生者产生消息,把消息交给交换机
- 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费
[外链图片转存中…(img-W8CC7HCA-1645611005682)]
6.远程模式
- RPC(不常用,不做解释)
- RPC远程调用模式
[外链图片转存中…(img-fmc0PNZd-1645611005682)]
RabbitMQ 其他内容
http://www.cnblogs.com/zhangweizhong/category/855479.html
点击快速跳转 ↩︎