深入解读RabbitMQ工作原理及简单使用
RabbitMQ系列目录
- RabbitMQ在Ubuntu上的环境搭建
- 深入解读RabbitMQ工作原理及简单使用
- Rabbit的几种工作模式介绍与实践
- Rabbit事务与消息确认
- Rabbit集群搭建
- 使用HAProxy为RabbitMQ搭建负载均衡
- REST API控制Rabbit
RabbitMQ简介
在介绍RabbitMQ之前实现要介绍一下MQ,MQ是什么?
MQ全称是Message Queue,可以理解为消息队列的意思,简单来说就是消息以管道的方式进行传递。
RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用Erlang语言的。
使用场景
在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中,而不是像几年前一样页面卡死或报错给用户。
像这种排队结算就用到了消息队列机制,放入通道里面一个一个结算处理,而不是某个时间断突然涌入大批量的查询新增把数据库给搞宕机,所以RabbitMQ本质上起到的作用就是削峰填谷,为业务保驾护航。
为什么选择RabbitMQ
现在的市面上有很多MQ可以选择,比如ActiveMQ、ZeroMQ、Appche Qpid,那问题来了为什么要选择RabbitMQ?
- 除了Qpid,RabbitMQ是唯一一个实现了AMQP标准的消息服务器;
- 可靠性,RabbitMQ的持久化支持,保证了消息的稳定性;
- 高并发,RabbitMQ使用了Erlang开发语言,Erlang是为电话交换机开发的语言,天生自带高并发光环,和高可用特性;
- 集群部署简单,正是应为Erlang使得RabbitMQ集群部署变的超级简单;
- 社区活跃度高,根据网上资料来看,RabbitMQ也是首选;
工作机制
生产者、消费者和代理
在了解消息通讯之前首先要了解3个概念:生产者、消费者和代理。
生产者:消息的创建者,负责创建和推送数据到消息服务器;
消费者:消息的接收方,用于处理数据和确认消息;
代理:就是RabbitMQ本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
消息发送原理
首先你必须连接到Rabbit才能发布和消费消息,那怎么连接和发送消息的呢?
你的应用程序和Rabbit Server之间会创建一个TCP连接,一旦TCP打开,并通过了认证,认证就是你试图连接Rabbit之前发送的Rabbit服务器连接信息和用户名和密码,有点像程序连接数据库,使用Java有两种连接认证的方式,后面代码会详细介绍,一旦认证通过你的应用程序和Rabbit就创建了一条AMQP信道(Channel)。
信道是创建在“真实”TCP上的虚拟连接,AMQP命令都是通过信道发送出去的,每个信道都会有一个唯一的ID,不论是发布消息,订阅队列或者介绍消息都是通过信道完成的。
为什么不通过TCP直接发送命令?
对于操作系统来说创建和销毁TCP会话是非常昂贵的开销,假设高峰期每秒有成千上万条连接,每个连接都要创建一条TCP会话,这就造成了TCP连接的巨大浪费,而且操作系统每秒能创建的TCP也是有限的,因此很快就会遇到系统瓶颈。
如果我们每个请求都使用一条TCP连接,既满足了性能的需要,又能确保每个连接的私密性,这就是引入信道概念的原因。

你必须知道的Rabbit
想要真正的了解Rabbit有些名词是你必须知道的。
包括:ConnectionFactory(连接管理器)、Channel(信道)、Exchange(交换器)、Queue(队列)、RoutingKey(路由键)、BindingKey(绑定键)。
ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用;
Channel(信道):消息推送使用的通道;
Exchange(交换器):用于接受、分配消息;
Queue(队列):用于存储生产者的消息;
RoutingKey(路由键):用于把生成者的数据分配到交换器上;
BindingKey(绑定键):用于把交换器的消息绑定到队列上;
看到上面的解释,最难理解的路由键和绑定键了,那么他们具体怎么发挥作用的,请看下图:

关于更多交换器的信息,我们在后面再讲。
消息持久化
Rabbit队列和交换器有一个不可告人的秘密,就是默认情况下重启服务器会导致消息丢失,那么怎么保证Rabbit在重启的时候不丢失呢?答案就是消息持久化。
当你把消息发送到Rabbit服务器的时候,你需要选择你是否要进行持久化,但这并不能保证Rabbit能从崩溃中恢复,想要Rabbit消息能恢复必须满足3个条件:
- 投递消息的时候durable设置为true,消息持久化; 
- 消息已经到达持久化交换器上; 
- 消息已经到达持久化的队列; 
持久化工作原理
Rabbit会将你的持久化消息写入磁盘上的持久化日志文件,等消息被消费之后,Rabbit会把这条消息标识为等待垃圾回收。
持久化的缺点
消息持久化的优点显而易见,但缺点也很明显,那就是性能,因为要写入硬盘要比写入内存性能较低很多,从而降低了服务器的吞吐量,尽管使用SSD硬盘可以使事情得到缓解,但他仍然吸干了Rabbit的性能,当消息成千上万条要写入磁盘的时候,性能是很低的。
所以使用者要根据自己的情况,选择适合自己的方式。
虚拟主机
每个Rabbit都能创建很多vhost,我们称之为虚拟主机,每个虚拟主机其实都是mini版的RabbitMQ,拥有自己的队列,交换器和绑定,拥有自己的权限机制。
环境搭建
前文我们已经介绍了Ubuntu搭建RabbitMQ的步骤:RabbitMQ在Ubuntu上的环境搭建
如果你是在Windows10上去安装那就更简单了,先放下载地址:
Erlang/Rabbit Server百度网盘链接:https://pan.baidu.com/s/1TnKDV-ZuXLiIgyK8c8f9dg 密码:wct9
当然也可去Erlang和Rabbit官网去下,就是速度比较慢。我的百度云Rabbit最新版本:3.7.6,Erlang版本:20.2,注意:不要下载最新的Erlang,在Windows10上打开扩展插件有问题,打不开。
- 安装Erlang; 
- 安装Rabbit Server; 
- 进入安装目录\sbin下,使用命令“rabbitmq-plugins enable rabbitmq_management”启动网页管理插件; 
- 重启Rabbit服务; 
使用:http://localhost:15672进行测试,默认的登陆账号为:guest,密码为:guest
重复安装Rabbit Server的坑
如果不是第一次在Windows上安装Rabbit Server一定要把Rabbit和Erlang卸载干净之后,找到注册表:HKEY_LOCAL_MACHINE\SOFTWARE\Ericsson\Erlang\ErlSrv 删除其下的所有项。
不然会出现Rabbit安装之后启动不了的情况,理论上卸载的顺序也是先Rabbit在Erlang。
代码实现
java版实现,使用maven项目,创建可以查看:MyEclipse2017破解设置与maven项目搭建
项目创建成功之后,添加Rabbit Client jar包,只需要在pom.xml里面配置,如下信息:
- <dependency>
- <groupId>com.rabbitmq</groupId>
- <artifactId>amqp-client</artifactId>
- <version>4.7.0</version>
- </dependency>
java实现代码分为两个类,第一个是创建Rabbit连接,第二是应用类使用最简单的方式发布和消费消息。
Rabbit的连接,两种方式:
方式一:
- public static Connection GetRabbitConnection() {
- ConnectionFactory factory = new ConnectionFactory();
- factory.setUsername(Config.UserName);
- factory.setPassword(Config.Password);
- factory.setVirtualHost(Config.VHost);
- factory.setHost(Config.Host);
- factory.setPort(Config.Port);
- Connection conn = null;
- try {
- conn = factory.newConnection();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return conn;
- }
方式二:
- public static Connection GetRabbitConnection2() {
- ConnectionFactory factory = new ConnectionFactory();
- // 连接格式:amqp://userName:password@hostName:portNumber/virtualHost
- String uri = String.format("amqp://%s:%s@%s:%d%s", Config.UserName, Config.Password, Config.Host, Config.Port,
- Config.VHost);
- Connection conn = null;
- try {
- factory.setUri(uri);
- factory.setVirtualHost(Config.VHost);
- conn = factory.newConnection();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return conn;
- }
第二部分:应用类,使用最简单的方式发布和消费消息
- public static void main(String[] args) {
- Publisher(); // 推送消息
- Consumer(); // 消费消息
- }
- /**
- * 推送消息
- */
- public static void Publisher() {
- // 创建一个连接
- Connection conn = ConnectionFactoryUtil.GetRabbitConnection();
- if (conn != null) {
- try {
- // 创建通道
- Channel channel = conn.createChannel();
- // 声明队列【参数说明:参数一:队列名称,参数二:是否持久化;参数三:是否独占模式;参数四:消费者断开连接时是否删除队列;参数五:消息其他参数】
- channel.queueDeclare(Config.QueueName, false, false, false, null);
- String content = String.format("当前时间:%s", new Date().getTime());
- // 发送内容【参数说明:参数一:交换机名称;参数二:队列名称,参数三:消息的其他属性;参数四:消息主体】
- channel.basicPublish("", Config.QueueName, null, content.getBytes("UTF-8"));
- System.out.println("已发送消息:" + content);
- // 关闭连接
- channel.close();
- conn.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 消费消息
- */
- public static void Consumer() {
- // 创建一个连接
- Connection conn = ConnectionFactoryUtil.GetRabbitConnection();
- if (conn != null) {
- try {
- // 创建通道
- Channel channel = conn.createChannel();
- // 声明队列【参数说明:参数一:队列名称,参数二:是否持久化;参数三:是否独占模式;参数四:消费者断开连接时是否删除队列;参数五:消息其他参数】
- channel.queueDeclare(Config.QueueName, false, false, false, null);
- // 创建订阅器,并接受消息
- channel.basicConsume(Config.QueueName, false, "", new DefaultConsumer(channel) {
- @Override
- public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
- byte[] body) throws IOException {
- String routingKey = envelope.getRoutingKey(); // 队列名称
- String contentType = properties.getContentType(); // 内容类型
- String content = new String(body, "utf-8"); // 消息正文
- System.out.println("消息正文:" + content);
- channel.basicAck(envelope.getDeliveryTag(), false); // 手动确认消息【参数说明:参数一:该消息的index;参数二:是否批量应答,true批量确认小于index的消息】
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
代码里面已经写了很详细的注释,在这里也不过多的介绍了。
执行效果,如图:

联系邮箱:intdb@qq.com
深入解读RabbitMQ工作原理及简单使用的更多相关文章
- RabbitMQ系列(二)深入了解RabbitMQ工作原理及简单使用
		深入了解RabbitMQ工作原理及简单使用 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器Exchange介绍 ... 
- 深入了解RabbitMQ工作原理及简单使用
		深入了解RabbitMQ工作原理及简单使用 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器Exchange介绍 ... 
- Optaplanner规划引擎的工作原理及简单示例(2)
		开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ... 
- 【Tomcat】Tomcat工作原理及简单模拟实现
		Tomcat应该都不陌生,我们经常会把写好的代码打包放在Tomcat里并启动,然后在浏览器里就能愉快的调用我们写的代码来实现相应的功能了,那么Tomcat是如何工作的? 一.Tomcat工作原理 我们 ... 
- LVS负载均衡机制之LVS-DR模式工作原理以及简单配置
		本博文主要简单介绍一下LVS负载均衡集群的一个基本负载均衡机制:LVS-DR:如有汇总不当之处,请各位在评论中多多指出. LVS-DR原理: LVS的英文全称是Linux Virtual Server ... 
- JDBC【2】-- JDBC工作原理以及简单封装
		目录 1. 工作原理 1.1 加载驱动 1.1.1 类加载相关知识 1.1.2 为什么JDK 1.6之后不需要显示加载了? 1.2 驱动加载完成了,然后呢? 2. 简单封装 1. 工作原理 一般我们主 ... 
- Optaplanner规划引擎的工作原理及简单示例(1)
		在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来 ... 
- 1-趣味解读DNS工作原理——转载疯猫网络科技
		因为只要我们输入百度.腾讯.淘宝的名字,无论它们的服务器在哪里,历经多少轮查询,我们都能找到并访问之.这就是计算机网络中著名的域名系统DNS(Domain Name System),它能实现把一个网站 ... 
- Vue2源码解读 - 响应式原理及简单实现
		直接进入主题了,想必大家都知道实现vue响应式核心方法就是 Object.defineProperty,那就从它开始说 Object.defineProperty 缺点: 深度监听,需要递归到底,一次 ... 
随机推荐
- ITEXT5.5.8转html为pdf文档解决linux不显示中文问题
			在windows中支持中文,在linux中不显示中文. 解决方法:添加字体库 下载simsun.ttc字体文件,把这文件拷贝到Linux系统的 /usr/share/fonts/ 下就可以了. 
- SAP MM MM17里不能修改物料主数据'Purchasing Value Key'字段值?
			SAP MM MM17里不能修改物料主数据'Purchasing Value Key'字段值? 记得在D项目上线之前数据导入系统之后,业务提出一些物料采购视图里的’Purchasing value k ... 
- 大数据时代,Wyn Enterprise和您一起探讨CIO的困境和出路 ZT
			这是一篇知识分享帖,如果您致力于成为一名CIO,希望您能够阅读完,信息虽然简略,但我们依然希望可以帮到您. CIO:首席信息官 CIO是干什么的 一.经典的CIO主要负责什么 1.IT战略规划.IT预 ... 
- Tomcat_记一次tomcatwar包应用简单部署过程
			记一次tomcat war包应用简单部署过程 by:授客 QQ:1033553122 1. 实践环境 Linux apache-tomcat-7.0.73 2. 实践步骤 # 解压tomcat压缩 ... 
- (网页)JavaScript周末总结(一)
			本周学习的内容总结: 1. 2章在html中使用javascript. 2. 3章Javascript的基本概念. 3. 4章变量,作用域,内存问题. 1-1:2章以下内容: 1.包含javascri ... 
- C#-继承(十一)
			继承概念 承用于创建可重用.扩展和修改在其他类中定义的行为的新类 创建一个类的时候,不是要写全新的数据成员和成员函数,可以指定新的类继承一个已经存在的类的成员.已有的类称为基类,新的类称为派生类 派生 ... 
- Spring MVC 静态资源处理 (三)
			完整的项目案例: springmvc.zip 目录 实例 项目结构: 一.配置web.xml <?xml version="1.0" encoding="UTF-8 ... 
- Apache kylin 入门
			本篇文章就概念.工作机制.数据备份.优势与不足4个方面详细介绍了Apache Kylin. Apache Kylin 简介 1. Apache kylin 是一个开源的海量数据分布式预处理引擎.它通过 ... 
- c/c++ 多态的实现原理分析
			多态的实现原理分析 当类里有一个函数被声明成虚函数后,创建这个类的对象的时候,就会自动加入一个__vfptr的指针, __vfptr维护虚函数列表.如果有三个虚函数,则__vfptr指向的是第一个虚函 ... 
- python之bytes和string
			转自:https://www.cnblogs.com/skiler/p/6687337.html 1.bytes主要是给在计算机看的,string主要是给人看的 2.中间有个桥梁就是编码规则,现在大趋 ... 
