RabbitMQ的开发应用( 三 )


举个例子:当你声明了一个名为 “search-indexing-online” 的队列,AMQP 代理会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为 “search-indexing-online” 。所以当你希望将消息投递给“search-indexing-online”的队列时,指定投递信息包括:交换机名称为空字符串,路由键为“search-indexing-online”即可 。
因此 direct exchange 中的 default exchange 用法,体现出了消息队列的 point to point,感觉像是直接投递消息给指定名字的队列 。
2.2.持久化虽然我们要避免系统宕机,但是这种“不可抗力”总会有可能发生 。rabbitmq如果宕机了,再启动便是了,大不了有短暂时间不可用 。但如果你启动起来后,发现这个rabbitmq服务器像是被重置了,以前的exchange,queue和message数据都没了,那就太令人崩溃了 。不光业务系统因为无对应exchange和queue受影响,丢失的很多message数据更是致命的 。所以如何保证rabbitmq的持久化,在服务使用前必须得考虑到位 。
持久化可以提高RabbitMQ的可靠性,以防在异常情况(重启、关闭、宕机等)下的数据丢失 。RabbitMQ的持久化分为三个部分:交换器的持久化、队列的持久化和消息的持久化 。
2.2.1.exchange持久化exchange交换器的持久化是在声明交换器的时候,将durable设置为true 。
如果交换器不设置持久化,那么在RabbitMQ交换器服务重启之后,相关的交换器信息会丢失,不过消息不会丢失,但是不能将消息发送到这个交换器 。
spring中创建exchange时,构造方法默认设置为持久化 。
2.2.2.queue持久化队列的持久化在声明队列的时候,将durable设置为true 。
如果队列不设置持久化,那么RabbitMQ交换器服务重启之后,相关的队列信息会丢失,同时队列中的消息也会丢失 。
exchange和queue,如果一个是非持久化,另一个是持久化,中bind时会报错 。
spring中创建exchange时,构造方法默认设置为持久化 。
2.2.3.message持久化要确保消息不会丢失,除了设置队列的持久化,还需要将消息设置为持久化 。通过将消息的投递模式(BasicProperties中的deliveryMode属性)设置为2即可实现消息的持久化 。
· 持久化的消息在到达队列时就被写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,只有在内存吃紧的时候才会从内存中清除 。
· 非持久化的消息一般只保存在内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存空间 。
如果将所有的消息都进行持久化操作,这样会影响RabbitMQ的性能 。写入磁盘的速度比写入内存的速度慢很,所以要在可靠性和吞吐量之间做权衡 。
在spring中,BasicProperties中的deliveryMode属性,对应的是MessageProperties中的deliveryMode 。平时使用的
RabbitTemplate.convertAndSend()方法默认设置为持久化,deliveryMode=2 。如果需要设置非持久化发送消息,需要手动设置:
messageProperties.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
2.2.4.完整方案一、exchange、queue、message
要保证消息的持久化,在rabbitmq本身的结构上需要实现下面这些:
exchange交换机的durable设置为true 。
queue队列的durable设置为true 。
message消息的投递模式deliveryMode设置为2 。
二、发布确认
前面是保证了消息在投递到rabbitmq中,如何保证rabbit中消息的持久化 。
那么还需要保证生产者能成功发布消息,如交换机名字写错了等等 。可以在发布消息时设置投递成功的回调,确定消息能成功投递到目标队列中 。
三、接收确认
对于消费者来说,如果在订阅消息的时候,将autoAck设置为true,那么消费者接收到消息后,还没有处理,就出现了异常挂掉了,此时,队列中已经将消息删除,消费者不能够在收到消息 。
这种情况可以将autoAck设置为false,进行手动确认 。
四、镜像队列集群
在持久化后的消息存入RabbitMQ之后,还需要一段时间才能存入磁盘 。RabbitMQ并不会为每条消息都进行同步存盘,可能仅仅是保存到操作系统缓存之中而不是物理磁盘 。如果在这段时间,服务器宕机或者重启,消息还没来得及保存到磁盘当中,从而丢失 。对于这种情况,可以引入RabiitMQ镜像队列机制 。
这里强调是镜像队列集群,而非普通集群 。因为出于同步效率考虑,普通集群只会同步队列的元数据,而不会同步队列中的消息 。只有升级成镜像队列集群后,才能也同步消息 。


推荐阅读