1 RabbitMQ消息队列的小伙伴: ProtoBuf(Google Protocol Buffer)

什么是ProtoBuf?

一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

它可以作为RabbitMQ的Message的数据格式进行传输,由于是结构化的数据,这样就极大的方便了Consumer的数据高效处理。当然了你可能说使用XML不也可以吗?与XML相比,ProtoBuf有以下优势:

  1. 简单
  2. size小了3-10倍
  3. 速度快乐20-100倍
  4. 易于编程
  5. 减小了语义的歧义

当然了,的确还有很多类似的技术,比如JSON,Thrift等等,和他们相比,ProtoBuf的优势或者劣势在哪里?简单说来,ProtoBuf就是简单,快。以测试为证:项目 thrift-protobuf-compare 比较了这些类似的技术,下图 显示了该项目的一项测试结果。

在占用空间上的性能比较:

由此可见,ProtoBuf具有速度和空间的优势,使得它现在应用非常广泛。比如Hadoop就使用了它。

2 Publisher的消息确认机制

在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack。那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consumer处理呢?毕竟对于一些非常重要的数据,可能Publisher需要确认某个消息已经被正确处理。

在我们的系统中,我们没有是实现这种确认,也就是说,不管Message是否被Consume了,Publisher不会去care。他只是将自己的状态publish给上层,由上层的逻辑去处理。如果Message没有被正确处理,可能会导致某些状态丢失。但是由于提供了其他强制刷新全部状态的机制,因此这种异常情况的影响也就可以忽略不计了。

对于某些异步操作,比如客户端需要创建一个FileSystem,这个可能需要比较长的时间,甚至要数秒钟。这时候通过RPC可以解决这个问题。因此也就不存在Publisher端的确认机制了。

那么,有没有一种机制能保证Publisher能够感知它的Message有没有被处理的?答案肯定的。在这里感谢笑天居士同学:他在我的《RabbitMQ消息队列(三):任务分发机制》文后留言一起讨论了问题,而且也查找了一些资料。在这里我整理了一下他转载和一篇文章和原创的一篇文章。衔接已经附后。

1). 事务机制 VS Publisher Confirm

如果采用标准的 AMQP 协议,则唯一能够保证消息不会丢失的方式是利用事务机制 -- 令 channel 处于 transactional 模式、向其 publish 消息、执行 commit 动作。在这种方式下,事务机制会带来大量的多余开销,并会导致吞吐量下降 250% 。为了补救事务带来的问题,引入了 confirmation 机制(即 Publisher Confirm)。

为了使能 confirm 机制,client 首先要发送 confirm.select 方法帧。取决于是否设置了 no-wait 属性,broker 会相应的判定是否以 confirm.select-ok 进行应答。一旦在 channel 上使用 confirm.select方法,channel 就将处于 confirm 模式。处于 transactional 模式的 channel 不能再被设置成 confirm 模式,反之亦然。
    一旦 channel 处于 confirm 模式,broker 和 client 都将启动消息计数(以 confirm.select 为基础从 1 开始计数)。broker 会在处理完消息后,在当前 channel 上通过发送 basic.ack 的方式对其进行 confirm 。delivery-tag 域的值标识了被 confirm 消息的序列号。broker 也可以通过设置 basic.ack 中的 multiple 域来表明到指定序列号为止的所有消息都已被 broker 正确的处理了。

在异常情况中,broker 将无法成功处理相应的消息,此时 broker 将发送 basic.nack 来代替 basic.ack 。在这个情形下,basic.nack 中各域值的含义与 basic.ack 中相应各域含义是相同的,同时 requeue 域的值应该被忽略。通过 nack 一或多条消息,broker 表明自身无法对相应消息完成处理,并拒绝为这些消息的处理负责。在这种情况下,client 可以选择将消息 re-publish 。


channel 被设置成 confirm 模式之后,所有被 publish 的后续消息都将被 confirm(即 ack) 或者被 nack
一次。但是没有对消息被 confirm 的快慢做任何保证,并且同一条消息不会既被 confirm 又被 nack 。

2. 消息在什么时候确认

broker 将在下面的情况中对消息进行 confirm :

  • broker 发现当前消息无法被路由到指定的 queues 中(如果设置了 mandatory 属性,则 broker 会先发送 basic.return)
  • 非持久属性的消息到达了其所应该到达的所有 queue 中(和镜像 queue 中)
  • 持久消息到达了其所应该到达的所有 queue 中(和镜像 queue 中),并被持久化到了磁盘(被 fsync)
  • 持久消息从其所在的所有 queue 中被 consume 了(如果必要则会被 acknowledge)

broker 会丢失持久化消息,如果 broker 在将上述消息写入磁盘前异常。在一定条件下,这种情况会导致 broker 以一种奇怪的方式运行。例如,考虑下述情景:

1.  一个 client 将持久消息 publish 到持久 queue 中
   2.  另一个 client 从 queue 中 consume 消息(注意:该消息具有持久属性,并且 queue 是持久化的),当尚未对其进行 ack
   3.  broker 异常重启
   4.  client 重连并开始 consume 消息

在上述情景下,client 有理由认为消息需要被(broker)重新 deliver 。但这并非事实:重启(有可能)会令 broker
丢失消息。为了确保持久性,client 应该使用 confirm 机制。如果 publisher 使用的 channel 被设置为
confirm 模式,publisher 将不会收到已丢失消息的 ack(这是因为 consumer 没有对消息进行 ack
,同时该消息也未被写入磁盘)。

3. 编程实现

首先要区别AMQP协议mandatory和immediate标志位的作用。

mandatory和immediate是AMQP协议中basic.pulish方法中的两个标志位,它们都有当消息传递过程中不可达目的地时将消息返回给生产者的功能。具体区别在于:
1. mandatory标志位
当mandatory标志位设置为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返还给生产者;当mandatory设为false时,出现上述情形broker会直接将消息扔掉。
2. immediate标志位
当immediate标志位设置为true时,如果exchange在将消息route到queue(s)时发现对应的queue上没有消费者,那么这条消息不会放入队列中。当与消息routeKey关联的所有queue(一个或多个)都没有消费者时,该消息会通过basic.return方法返还给生产者。

具体的代码参考请参考参考资料1.

参考资料:

1. http://blog.csdn.net/jiao_fuyou/article/details/21594205

2. http://blog.csdn.net/jiao_fuyou/article/details/21594947

3.  http://my.oschina.net/moooofly/blog/142095

RabbitMQ其他(八)的更多相关文章

  1. 消息中间件——RabbitMQ(八)高级特性全在这里!(下)

    前言 上一篇消息中间件--RabbitMQ(七)高级特性全在这里!(上)中我们介绍了消息如何保障100%的投递成功?,幂等性概念详解,在海量订单产生的业务高峰期,如何避免消息的重复消费的问题?,Con ...

  2. RabbitMQ系列(八)--顺序消费模式和迅速消息发送模式

    MQ使用过程中,有些业务场景需要我们保证顺序消费,而如果一个Producer,一个Queue,多个Consumer的情况下是无法保证顺序的 举例: 1.业务上产生三条消息,分别是对数据的增加.修改.删 ...

  3. 消息中间件——RabbitMQ(九)RabbitMQ整合Spring AMQP实战!(全)

    前言 1. AMQP 核心组件 RabbitAdmin SpringAMQP声明 RabbitTemplate SimpleMessageListenerContainer MessageListen ...

  4. 消息中间件——RabbitMQ(十)RabbitMQ整合SpringBoot实战!(全)

    前言 1. SpringBoot整合配置详解 publisher-confirms,实现一个监听器用于监听Broker端给我们返回的确认请求:RabbitTemplate.ConfirmCallbac ...

  5. Python招聘需求与技能体系

    目前国内的招聘Python,基本都是偏向web后台开发,偶有高大上的数据挖掘&机器学习. 这是之前(2012年)找工作整理的一些JD,在梳理几年来的笔记,顺带理一理 可以以此建立自己的技能体系 ...

  6. Python后端开发要求

    关于Python后端开发要求 一.对Python有兴趣,熟悉Python(标准库) 最好阅读过源码 了解Python的优化(熟悉pypy更佳) 二.至少至少一门语言(不说"精通") ...

  7. Spring Boot(八):RabbitMQ详解

    Spring Boot(八):RabbitMQ详解 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用. 消息中间件在互联网公司的使用中越来越多 ...

  8. 八、Docker+RabbitMQ

    原文:八.Docker+RabbitMQ 一.下载镜像 docker pull rabbitmq:management 二.运行 docker run -d --name rabbitmq -e TZ ...

  9. (八)RabbitMQ消息队列-通过Topic主题模式分发消息

    原文:(八)RabbitMQ消息队列-通过Topic主题模式分发消息 前两章我们讲了RabbitMQ的direct模式和fanout模式,本章介绍topic主题模式的应用.如果对direct模式下通过 ...

  10. RabbitMQ入门教程(八):远程过程调用RPC

    原文:RabbitMQ入门教程(八):远程过程调用RPC 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.cs ...

随机推荐

  1. Multiple encodings set for module chunk explatform "GBK" will be used by compiler

    项目用idea启动的时候,突然报了个这个 Multiple encodings set for module explatform "GBK" will be used by co ...

  2. 更新表中数据可以使用join

    1.在修改数据库的时候,每写完一条SQL语句都要加上一个分号,这样每句之间是有依赖关系的,上面执行不成功不会执行下面的语句. 2.在更新数据库中数据时可以使用join. 例如: update res ...

  3. http超文本传输协议,get与post区别

    一:什么是http? http:超文本传输协议(HTTP,HyperText Transfer Protocol),是一个客户端和服务器端传输的标准,是应用层通信协议.客户端是中端用户,服务器端是网站 ...

  4. CCTableView

    今天用到TableView, 我就来记录一下....这些都是在网上找到了资料 //首先 继承 : public cocos2d::extension::CCTableViewDelegate,publ ...

  5. Jmeter使用流程及简单分析监控(转载)

    转载自:https://www.cnblogs.com/linglingyuese/archive/2013/03/04/linglingyuese-one.html#undefined 一.安装Jm ...

  6. c++之旅:操作符重载

    操作符重载 操作符重载可以为操作符添加更多的含义,操作符重载的作用的对象是类 那些操作符可以重载 除了下面几个操作符不能重载外,其它的操作符都能重载 . :: .* ?: sizeof 操作符重载的本 ...

  7. Selenium+Python常见定位方法

    参见官网:http://selenium-python.readthedocs.io/locating-elements.html 有多种策略来定位页面中的元素.你可以使用最适合你的情况.Seleni ...

  8. asp.net发送短信

    public class SmsServiceManager { public static string Send(string PhoneNumber, out string sendNo) { ...

  9. 为什么Eureka Client获取服务实例这么慢

    1. Eureka Client注册延迟 Eureka Client启动后不会立即向Eureka Server注册,而是有一个延迟时间,默认为40s 2. Eureka Server更新响应缓存 Eu ...

  10. 20145312 《Java程序设计》第2周学习总结

    20145312 <Java程序设计>第2周学习总结 教材学习内容总结 1.1类型.变量与运算符 1.1.1类型 1.基本类型 整数:short(2字节).long(8字节).int(4字 ...