欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。

欢迎跳转到本文的原文链接:https://honeypps.com/mq/why-is-queueingconsumer-marked-deprecated/


QueueingConsumer在Rabbitmq客户端3.x版本中用的如火如荼,但是在4.x版本开初就被标记为@Deprecated,这是为什么呢?本文就此展开探讨。

在我的博文《RabbitMQ之Consumer消费模式(Push & Pull)》中讲到,Consumer的消费模式有Pull 和 Push两种,而经常用到的就是Push模式,Push模式在3.x的用法demo如下:

QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicQos(1);
channel.basicConsume(QUEUE_NAME, false, "consumer_zzh",consumer);while (true) {QueueingConsumer.Delivery delivery = consumer.nextDelivery();String message = new String(delivery.getBody());System.out.println(" [X] Received '" + message + "'");channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);break;
}

在官方文档中推荐使用继承DefaultConsumer的方式:

boolean autoAck = false;
channel.basicConsume(queueName, autoAck, "myConsumerTag",new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag,Envelope envelope,AMQP.BasicProperties properties,byte[] body)throws IOException{String routingKey = envelope.getRoutingKey();String contentType = properties.getContentType();long deliveryTag = envelope.getDeliveryTag();// (process the message components here ...)channel.basicAck(deliveryTag, false);}
});

在源码注释中有关QueueingConsumer的介绍有这样一段:

QueueingConsumer was introduced to allow applications to overcome a limitation in the way Connection managed threads and consumer dispatching. When QueueingConsumer was introduced, callbacks to Consumers ware made on the Connection’s thread. This had two main drawbacks. Firstly, the Consumer could stall the processing of all Channels on the Connection. Secondly, if a Consumer made a recursive synchronous call into its Channel the Client would deadlock.

QueuingConsumer provided client code with an easy way to obviate the problem by queueing incoming messages and processing them on a separate, application-managed thread.

The threading behaviour of Connection and Channel has been changed so that each Channel uses a distinct thread for dispatching to Consumers. This prevents Consumers on one Channel holding up Consumers on another and it also prevents recursive calls from deadlocking the client.

As such, it is now safe to implement Consumer directly of to extend DefaultConsumer and QueueingConsumer is a lot less relevant.

上面提及了两个drawbacks:

  1. the Consumer could stall the processing of all Channels on the Connection. =>QueueingConsumer会拖累Connection的所有Channels的操作
  2. if a Consumer made a recursive synchronous call into its Channel the Client would deadlock.=>同步递归调用时会产生死锁

对于这两句简单的言辞,博主没有停下追求真理的脚步,既而去github上发问,当我咨询rabbitmq-java-client的作者时(issue @265),他是这么回复的:

Search rabbitmq-users archives. That consumer implementation was merely a workaround for the consumer operation dispatch deficiency that no longer exists. It has significant limitations in that automatic connection recovery does not support it and when deliveries happen faster than consumers actually process them, its internal j.u.c. queue data structure can grow very large. It has been deprecated for years prior to the removal.

上面提及的rabbitmq-users的链接是:https://groups.google.com/forum/#!forum/rabbitmq-users,当然在我大天朝是访问不了的。博主的翻墙软件也失效了,就没法search,有兴趣的小伙伴search到的话麻烦告知下(下方留言,私信,或者留下你的资料地址~)。不过作者也提交了两点:1. automatic connection recovery不支持QueueingConsumer的这种形式;2. 内存溢出问题。

对于QueueingConsumer的内存溢出问题,我在博文《[九]RabbitMQ-客户端源码之Consumer》中讲到QueueingConsumer内部其实是一个LinkBlockingQueue,它将从broker端接受到的信息先暂存到这个LinkBlockingQueue中,然后消费端程序在从这个LinkBlockingQueue中take出消息。试下一下,如果我们不take消息或者说take的非常慢,那么LinkBlockingQueue中的消息就会越来越多,最终造成内存溢出。

这里我们来看一段英文介绍: You have a queue in RabbitMQ. You have some clients consuming from that queue. If you don’t set a Qos setting at all (Basic.Qos), then RabbitMQ will push all the queue’s messages to the client as fast as the network and the clients will allow. 也就是说,如果由于某些原因,队列中堆积了比较多的消息,就可能导致Consumer内存溢出卡死,于是发生恶性循环,队列消息不断堆积得不到消化,彻底地悲剧了。其实这个问题可以通过设置Basic.Qos来很好的解决。

博主这里实验了下,先往一个queue里发送200MB+的消息,然后进行消费:

QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(QUEUE_NAME, false, consumer);

在client端发送Basic.Consume帧,并设置回调函数为QueueingConsumer之后,并不真正消费QueueingConsumer中的LinkedBlockingQueue中的内容,通过JVisualVM可以看到堆内存的变化,如下图所示:

可以看到堆内存一直在增加,博主这里只测试了发送200+MB的消息,如果发送的更多,那么这个堆会变得更大,直到OutOfMemory。

在stackoverflow上也有关于QueueingConsumer的疑问,有人说QueueingConsumer不是event-driven的,也有人提及了内存溢出的问题。看来QueueingConsumer的毛病真的不少,都推荐使用继承DefaultConsumer的方式进行消费。

如果博主后面搜集到更多的证据,也会在本博文中更新相关的内容。
如果你有相关的资料,也可以留言分享一下,大家互相促进~~


参考资料

  1. RabbitMQ之Consumer消费模式(Push & Pull)
  2. [九]RabbitMQ-客户端源码之Consumer
  3. RabbitMQ-api-guide
  4. 解决RabbitMQ队列超长QueueingConsumer导致JVM内存溢出的问题

欢迎跳转到本文的原文链接:https://honeypps.com/mq/why-is-queueingconsumer-marked-deprecated/


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


为什么QueueingConsumer会被Deprecated?相关推荐

  1. 消息中间件(Kafka/RabbitMQ)收录集

    本篇主要整理工作中遇到的一些消息中间件的相关知识,包括Kafka, RabbitMQ, RocketMQ, ActiveMQ等,不排除收录其他消息中间件的可能. 这里会持续收录相关知识,包括安装.部署 ...

  2. RabbitMQ优秀博文整理

    1.关于主流MQ的介绍,各MQ的优缺点.MQ的使用选举等 MQ消息队列详解.四大MQ的优缺点分析_从百草园杀到三味书屋&的博客-CSDN博客_几种消息队列的优缺点 2.RabbitMQ常见问题 ...

  3. DeprecationWarning: the md5 module is deprecated; use hashlib instead import md5

    原来的python脚本是在Ubuntu 14.04 64bit上写的,运行没有问题,但是在CentOS 6.3上的crontab中定时执行时,每次都报 DeprecationWarning: the ...

  4. 【FFmpeg】解决警告warning: xxx is deprecated [-Wdeprecated-declarations]的方法

    1.问题描述 编译FFmpeg程序时,经常报一些关于"deprecated"的警告信息,具体内容如下: decode.cpp:28:2: warning: 'void av_reg ...

  5. php解决 mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysq

    微信小程序开发交流qq群   173683895    承接微信小程序开发.扫码加微信. The mysql extension is deprecated and will be removed i ...

  6. 解决 The mysql extension is deprecated and will be r

    为什么80%的码农都做不了架构师?>>>    解决 The mysql extension is deprecated and will be removed in the fut ...

  7. neutron CLI is deprecated and will be removed in the future. Use openstack CLI instead

    1.现象描述: 以前在测试环境中使用过icehouse版本,记得当时查看网络列表是使用neutron net-list,最近两天在测试openstack ocata的时候发现好多之前的命令都不能正常使 ...

  8. C++关键字deprecated

    指示声明有此属性的名字或实体被弃用,即允许但因故不鼓励使用. 语法 [[deprecated]] [[deprecated( 字符字面量 )]] 解释 指示允许使用声明有此属性的名称或实体,但因故不鼓 ...

  9. Pandas 中的这些函数/属性将被 deprecated

    作者 | luanhz 来源丨小数志 导读 Pandas对于日常数据分析和处理来说是最常用的工具(没有之一),笔者之前也总结分享了很多相关用法和技巧. 与之不同,今天本文来介绍几个已经在函数文档中列入 ...

最新文章

  1. 利用边缘灰度变化建模,来提高圆环直径求取精度
  2. 开发日记-20190502 关键词 汇编语言(一)
  3. [python-图像处理]python图片处理技巧[基本操作]
  4. 小程序子组件向父组件传值_一套组件化、可复用、易扩展的微信小程序 UI 组件库...
  5. android+button+不可点击置灰,android:tint 给imagebutton着色 按钮灰色
  6. ubuntu linux本地源,如何制作UbuntuLinux操作系统的本地源?
  7. 行为设计模式 - 迭代器设计模式
  8. linux更新硬件时钟,Linux内核“11分钟模式”可以做的最大的硬件时钟更新是什么?...
  9. Python使用爬山算法寻找序列“最大值”
  10. winform窗体MaximizeBox
  11. Win10+Linux双系统删除Linux
  12. 软件测试工程师的自我认识和定位!!
  13. blender布尔运算差值看不出效果/blender布尔差值无效
  14. linux从源码编译cairo,如何在windows下编译cairo
  15. 给正在排版毕业论文的你:高校毕业论文Latex格式排版模版
  16. 1. STM32学习 STM32综述
  17. 内部存储器——①静态存储器
  18. Android 中自定义ViewGroup实现流式布局的效果
  19. Linux多功能下载机(Arias2)
  20. linux驱动 设备驱动模型

热门文章

  1. Spring的@Configuration配置类-Full和Lite模式
  2. mybatis-plus根据多个字段排序_Mybatis Plus学习笔记(逻辑删除/动态填充/常用插件)...
  3. jmeter之ip欺骗
  4. Android ViewPager
  5. 独立思考者模型:寻找潜藏在表象背后的真相 探寻真相的方法
  6. 在64位Windows中使用64位版本ASP.NET 2.0的问题
  7. 中石油训练赛 - Incremental Induction(贪心)
  8. CodeForces - 1326E Bombs(线段树+思维)
  9. CH - 6201 走廊泼水节(最小生成树,水题)
  10. HDU - 1528 Card Game Cheater(二分图最大匹配)