目录
4.3 发布/订阅模式(Publish/Subscribe)
1. 什么是 RabbitMQ
1.1 MQ(Message Queue)消息队列
消息队列中间件,是分布式系统中的重要组件;
主要解决异步处理、应用解耦、流量削峰等问题,从而实现高性能,高可用,可伸缩和最终一致性的架构。
使用较多的消息队列产品:RabbitMQ,RocketMQ,ActiveMQ,ZeroMQ,Kafka 等。
1.1.1 异步处理
用户注册后,需要发送验证邮箱和手机验证码。
将注册信息写入数据库,发送验证邮件,发送手机,三个步骤全部完成后,返回给客户端。
1.1.2 应用解耦
场景:订单系统需要通知库存系统。
如果库存系统异常,则订单调用库存失败,导致下单失败。
原因:订单系统和库存系统耦合度太高。
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户,下单成功。
库存系统:订阅下单的消息,获取下单信息,库存系统根据下单信息,再进行库存操作。
假如:下单的时候,库存系统不能正常运行,也不会影响下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了,实现了订单系统和库存系统的应用解耦。
所以,消息队列是典型的“生产者-消费者“模型。
生产者不断的向消息队列中生产消息,消费者不断的从队列中获取消息。
因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的入侵,这样就实现了生产者和消费者的解耦。
1.1.3 流量削峰
抢购,秒杀等业务,针对高并发的场景。
因为流量过大,暴增会导致应用挂掉,为解决这个问题,在前端加入消息队列。
用户的请求,服务器接收后,首先写入消息队列,如果超过队列的长度,就抛弃,发送一个秒杀结束的页面;而请求成功(秒杀成功)的就是进入队列的用户!
1.2 背景知识介绍
1.2.1 AMQP 高级消息队列协议
Advanced Message Queuing Protocol 是一个提供统一消息服务的应用层标准高级消息队列协议。
协议:数据在传输的过程中必须要遵守的规则。
基于此协议的客户端可以与消息中间件传递消息。
并不受产品、开发语言等条件的限制。
1.2.2 JMS
Java Message Server 是 Java 消息服务应用程序接口,一种规范,和 JDBC 担任的角色类似。
JMS 是一个 Java 平台中关于面向消息中间件的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
1.2.3 二者的联系
JMS 是定义了统一接口,统一消息操作;AMQP 通过协议统一数据交互格式。
JMS 必须是 Java 语言;AMQP 只是协议,与语言无关。
1.2.4 Erlang 语言
Erlang 是一种通用的面向并发的编程语言,目的是创造一种可以应对大规模并发活动的编程语言和运行环境。
最初是专门为通信应用设计的,比如控制交换机或者变换协议等,因此非常适合构建分布式,实时软并行计算系统。
Erlang 运行时环境是一个虚拟机,有点像 Java 的虚拟机,这样代码一经编译,同样可以随处运行。
1.3 为什么选择 RabbitMQ
RabbitMQ 由 Erlang 开发,AMQP 的最佳搭档,安装部署简单,上手门槛低。
企业级消息队列,经过大量实践考验的高可靠,大量成功的应用案例,例如阿里、网易等一线大厂都有使用。
有强大的 WEB 管理页面。
强大的社区支持,为技术进步提供动力。
支持消息持久化、支持消息确认机制、灵活的任务分发机制等,支持功能非常丰富。
集群扩展很容易,并且可以通过增加节点实现成倍的性能提升。
总结:如果希望使用一个可靠性高、功能强大、易于管理的消息队列系统那么就选择 RabbitMQ;如果想用一个性能高,但偶尔丢点数据,可以使用 Kafka 或者 ZeroMQ。
Kafka 和 ZeroMQ 的性能比 RabbitMQ 好很多。
1.4 RabbitMQ 各组件功能
RabbitMQ 组件
1. Broker - 消息队列服务器实体。
2. Virtual Host - 虚拟主机:
- 标识一批交换机、消息队列和相关对象,形成的整体。
- 虚拟主机是共享相同的身份认证和加密环境的独立服务器域。
- 每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。
- VHost 是 AMQP 概念的基础,RabbitMQ 默认的 vhost 是 /,必须在链接时指定。
3. Exchange - 交换器(路由):用来接收生产者发送的消息并将这些消息通过路由发给服务器中的队列。
4. Banding - 绑定。用于交换机和消息队列之间的关联
5. Queue - 消息队列:
- 用来保存消息直到发送给消费者。
- 它是消息的容器,也是消息的终点。
- 一个消息可投入一个或多个队列。
- 消息一直在队列里面,等待消费者连接到这个队列将其取走。
6. Channel - 通道(信道):
- 多路复用连接中的一条独立的双向数据流通道。
- 信道是建立在真实的 TCP 连接内的虚拟链接。
- AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,都是通过信道完成的。
- 因为对于操作系统来说,建立和销毁 TCP 连接都是非常昂贵的开销,所以引入了信道的概 念,用来复用 TCP 连接。
7. Connection - 网络连接,比如一个 TCP 连接。
8. Publisher - 消息的生产者,也是一个向交换器发布消息的客户端应用程序。
9. Consumer - 消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
10. Message - 消息:
- 消息是不具名的,它是由消息头和消息体组成。
- 消息体是不透明的,而消息头则是由一系列的可选属性组成,这些属性包括 routing-key(路由键)、priority(优先级)、delivery-mode(消息可能需要持久性存储[消息的路由模式])等。
2. 使用 RabbitMQ
想要安装 RabbitMQ,必须先安装 erlang 语言环境;类似安装 tomcat,必须先安装 JDK。
查看匹配的版本:RabbitMQ Erlang Version Requirements — RabbitMQ
2.1 RabbitMQ 安装启动
Erlang 下载:Erlang 下载
Socat 下载:Socat下载
RabbitMQ 下载:RabbitMQ 下载
2.1.1 RabbitMQ安装
启动 Linux 系统(192.168.186.128),传输相关的三个 rpm 到 /opt 目录下,然后在 /opt 目录下按顺序执行安装命令:
[root@localhost opt]# rpm -ivh erlang-21.3.8.16-1.el7.x86_64.rpm
[root@localhost opt]# rpm -ivh socat-1.7.3.2-5.el7.lux.x86_64.rpm
[root@localhost opt]# rpm -ivh rabbitmq-server-3.8.6-1.el7.noarch.rpm
2.1.2 启动后台管理插件
[root@localhost opt]# rabbitmq-plugins enable rabbitmq_management
2.1.3 启动和关闭 RabbitMQ
[root@localhost opt]# systemctl start rabbitmq-server.service
[root@localhost opt]# systemctl status rabbitmq-server.service
[root@localhost opt]# systemctl restart rabbitmq-server.service
[root@localhost opt]# systemctl stop rabbitmq-server.service
2.1.4 查看进程
[root@localhost opt]# ps -ef | grep rabbitmq
2.1.5 测试
- 关闭防火墙
systemctl stop firewalld
(或者防火墙开放对应的端口号)
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --zone=public --add-port=5671/tcp --permanent
firewall-cmd --zone=public --add-port=5672/tcp --permanent
firewall-cmd --zone=public --add-port=25672/tcp --permanent
firewall-cmd --reload
- 浏览器输入:http://ip:15672(比如这里输入:http://192.168.186.128:15672)
- 默认帐号和密码是 guest,而 guest 用户默认不允许远程连接
创建账号:
[root@localhost opt]# rabbitmqctl add_user zm 123456
设置用户角色:
[root@localhost opt]# rabbitmqctl set_user_tags zm administrator
设置用户权限:
[root@localhost opt]# rabbitmqctl set_permissions -p "/" zm ".*" ".*" ".*"
查看当前用户和角色:
[root@localhost opt]# rabbitmqctl list_users
修改用户密码:
[root@localhost opt]# rabbitmqctl change_password zm NewPassword
管理界面介绍:
- Overview - 概览
- Connections - 查看链接情况
- Channels - 信道(通道)情况
- Exchanges - 交换机(路由)情况,默认4类7个
- Queues - 消息队列情况
- Admin - 管理员列表
- RabbitMQ 提供给编程语言客户端链接的端口 :5672;
- RabbitMQ 管理界面的端口 :15672;
- RabbitMQ 集群的端口 :25672。
3. RabbitMQ 快速入门
依赖
<!-- 指定编码及版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
日志依赖 log4j(可选项)
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=rebbitmq.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
log4j.rootLogger=debug, stdout,file
创建连接
先在 RabbitMQ 管理界面 Admin
-> Virtual Hosts
-> Add a new virtual host
创建虚拟主机 (Name: /zm, Description: zm, Tags: administrator);
然后编写连接的代码:
public class ConnectionUtil {
public static Connection getConnection() throws Exception{
// 1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2.在工厂对象中设置 MQ 的连接信息(ip, port, vhost, username, password)
factory.setHost("192.168.186.128");
factory.setPort(5672);
factory.setVirtualHost("/zm");
factory.setUsername("zm");
factory.setPassword("123456");
// 3.通过工厂获得与 MQ 的连接
return factory.newConnection();
}
public static void main(String[] args) throws Exception {
Connection connection = getConnection();
System.out.println("Connection: " + connection);
connection.close();
}
}
4. RabbitMQ 模式
RabbitMQ 提供了 6 种消息模型,但是第 6 种其实是 RPC,并不是 MQ。
在线手册:https://www.rabbitmq.com/getstarted.html
5 种消息模型,大体分为两类:
- 1 和 2 属于点对点。
- 3、4、5 属于发布订阅模式(一对多)。
点对点模式 - P2P(Point to Point)模式:
- 包含三个角色:消息队列 queue,发送者 sender,接收者 receiver。
- 每个消息发送到一个特定的队列中,接收者从中获得消息。
- 队列中保留这些消息,直到他们被消费或超时。
- 如果希望发送的每个消息都会被成功处理,那需要 P2P。
特点:
- 每个消息只有一个消费者,一旦消费,消息就不在队列中了。
- 发送者和接收者之间没有依赖性,发送者发送完成,不管接收者是否运行,都不会影响消息发送到队列中。
- 接收者成功接收消息之后需向对象应答成功(确认)。
发布订阅模式 - publish / subscribe 模式:
- Pub / Sub 模式包含三个角色:交换机 exchange,发布者 publisher,订阅者 subcriber。
- 多个