2.3 交换器和绑定
前面以及说明了什么是队列,消息如何投递到了队列中的呢?
当消息投递到队列中时,通过消息发送给交换器完成,队列通过路由键绑定到交换器。当消息发送到代理服务器时,消息将拥有一个路由键(即便是空的)rabbitmq也会将其和绑定使用的路由键进行匹配。匹配成功,则消息投递到该队列;不匹配,消息进入“黑洞”。
当消息需要投递到多个队列时,AMQP协议定义了四种不同类型的交换机,分别为:
- **direct:**路由器匹配,则消息被投递到对应队列
服务器必须为direct类型的交换器,包含一个空字符串名称的默认交换器。声明一个队列时,会自动绑定到默认交换器,队列名为路由键。前提是你已经获得了信道的实例:
$channel->basic_public(msg,'','queue-name')
msg为消息内容;第二个为空字符串,指定了默认交换器;第三个为路由键,声明队列名称。 - fanout: fanout交换器会将收到的消息广播绑定到队列上。其消息通信模式为:
当发生一条消息到fanout时,他会把消息投递给所有附加在此交换器的队列。其交换器消息流如下图
- topic: 此交换器能够使来自不同源头的消息到达同一个队列。
- headers: 此交换器允许你匹配AMQP消息的header而非路由键,除此之外headers路由器和direct 路由器完全一致,但是性能较差。故不太实用,且几乎再也用不到
2.4 多租户模式:虚拟主机和隔离
每一个rabbitmq服务器都能创建虚拟消息服务器,我们称之为虚拟主机(vhost)。 每个vhost本质上是一个mini的rabbitmq服务器,拥有自己的队列、交换器和绑定,重要的是它拥有自己的权限机制。保证了一个rabbitmq服务器可服务众多应用程序。vhost之于rabbit就像虚拟机和物理机一样:通过各个实例间提供逻辑分离,,允许为众多客户区分开来。有可避免队列和交换器命名冲突。
vhost是AMQP概念的基础,连接时指定。rabbitmq包含了开箱即用的rabbitmq的默认vhost:“/”,因此使用起来非常简单。权限控制是以vhost为单位的。
当rabbitmq创建用户时,用户通常会被指派至少一个vhost,且只能访问被指派的vhost内的队列、交换器和绑定。设计消息通信框架时vhost之间是绝对隔离的。
在rabbitmq集群上创建vhost时,整个集群上都会创建该vhost。
vhost基本操作:
通过rabbitmq安装目录下 ./sbin/ 目录下 rabbitmqctl 工具
创建: rabbitmqctl add_vhost[vhost_name]
删除: rabbitmqctl delete_vhost[vhost_name]
查看运行: rabbitmqctl list_vhosts
2.5 消息持久化和策略
能从AMQP服务器崩溃中恢复的消息,称之为持久化消息。
消息从rabbit崩溃中恢复。则必须满足以下三点:
-
将投递模式设置成2(持久)
-
发送到持久的交换器
-
到达持久化的队列
rabiitmq确保持久性消息能从服务器重启中恢复的方式是,就他们写入磁盘上的一个持久化日志文件。发布一条持久性消息到持久交换器上时,rabbit会在消息提交到日志文件后才发送相应。当持久化消息到达非持久化队列,会自动从持久性日志中删除。
在持久化队列中消费一条持久性消息是,会在持久化日志中吧这条消息标记为等待垃圾收集。消费持久化消息前,rabbitmq重启,服务器自动重建服务器和队列、绑定,重播持久性日志文件中的消息到合适的队列或者交换器中。
消息持久化的性能:消息存入磁盘比写入内存中慢很多。极大减少了rabbitmq每秒处理消息的数量。很多情况下 消息吞吐量可能降低10倍,且在集群中支持不是很好。
权衡取舍,要保证持久化消息通信,可运行两种类型的rabbit集群:非持久化消息通信、持久化消息通信的活动/热备非集群rabbitmq服务器
正常情况下,如果消息经过交换器进入队列就可以完成消息的持久化,但如果消息在没有到达broker之前出现意外,那就造成消息丢失,有没有办法可以解决这个问题?
RabbitMQ有两种方式来解决这个问题:
1. 通过AMQP提供的事务机制实现;
使用事务不但会降低2-10倍的消息吞吐量,且会使生产者和消费者同步。
事务的实现主要是对信道(Channel)的设置,主要的方法有三个:- channel.txSelect()声明启动事务模式;
- channel.txComment()提交事务;
- channel.txRollback()回滚事务;
2. 使用发送者确认模式实现;
Confirm发送方确认模式使用和事务类似,也是通过设置Channel进行发送方确认的。其实现方式主要有三个:- channel.waitForConfirms()普通发送方确认模式;
- channel.waitForConfirmsOrDie()批量确认模式;
- channel.addConfirmListener()异步监听发送方确认模式;
2.6 发送消息的过程
1.消息生产者发布消息的过程:
- 连接RabbitMq
- 获取信道
- 声明交换器
- 创建消息
- 发布消息
- 关闭信道
- 关闭连接
2.消费者接受消息的过程:
- 连接RabbitMq
- 获取信道
- 声明交换器
- 声明队列
- 把队列和交换器绑定起来
- 消息
- 关闭信道
- 关闭连接