之前文章说过,如果使用 RabbitMQ,尽可能使用框架,而不要去使用 RabbitMQ 提供的 Java 版客户端。

细说起来,其实还是因为 RabbitMQ 客户端的使用有很多的注意事项,稍微不注意,就容易翻车。

我是 2013 年就开始用起了 RabbitMQ,一路使用,一路和它一起成长。当时,由于用的早,市面上也没有特别成熟的 RabbitMQ 客户端框架。所以,不得已之下,只好自己做了一套客户端。

在这其中,正好也有了许多独特的经验也和大家分享一下,以免后来者陷入“后人哀之而不鉴之,亦使后人而复哀后人也”的套娃中。

一、那么,就先从网络连接开始吧

1. 应该长久生存的连接

在 RabbitMQ 中,由于需要客户端和服务器端进行握手,所以导致客户端和服务器端的连接如果要成功创建,需要很高的成本。

每一个连接的创建至少需要 7 个 TCP 包,这还只是普通连接。如果需要 TLS 的参与,则 TCP 包会更多。

而且,RabbitMQ 中主要是以 Channel 方式通信,所以,每次创建完 Connection 网络连接,还得创建 Channel,这又需要 2 个 TCP 包。

如果,每次用完,再把连接关闭,首先还要关闭已经创建的 Channel,这也需要 2 个 TCP 包。

然后,再关闭已经建立好的 Connection 连接,又需要 2 个 TCP 包。

咱们算算,如果一个连接从创建到关闭,一共需要多少个 TCP 包?

7 + 2 + 2 + 2 = 13

一共需要 13 个包。这个成本是很昂贵的。

所以,在 RabbitMQ 中,连接最好缓存起来,重复使用更好。

2. Channel 还是独占好

在 RabbitMQ 自己的客户端中,Channel 出于性能原因,并不是线程安全的。

而如果咱们为了线程共用,给 Channel 人为的在外部加上锁,本身就和 RabbitMQ 的 Channel 设计意图是冲突的。

所以,最好的办法就是一个线程一个 Channel。

3. Channel 最好也别关

就像连接应该缓存起来那样,Channel 的打开和关闭也需要时间成本,而且没有必要去重新创建 Channel,所以,Channel 也应该缓存起来重用。

4. 别把消费和发送的连接搞在一起

把消费和发送的连接搞在一起,这是个很容易犯的错误!

我们用 RabbitMQ 的时候,我们自己的系统本身大部分都是既要发消息也要收消息的。对于这种情况,有很多程序员走了极端:

他们觉得 RabbitMQ 连接成本高,所以省着用。于是就把发消息和收消息的连接混在一起,使用同一个 TCP 连接。

这很可能会埋一个大雷。

因为,当我们发消息很频繁的时候,我们收消息也是走的同一个 TCP 通道,收完了消息,客户端还要给 RabbitMQ 服务器端一个 ACK。

RabbitMQ 服务器端,对于每个 TCP 连接都会分配专门的进程,如果遇到这个进程繁忙,这个 ACK 很可能被丢弃,又或者等待处理的时间过长。而这种情况又会导致 RabbitMQ 中的未确认消息会被堆积的越来越多,影响到整套系统。

所以,消费和发送的连接必须分开,各干各的事情。

5. 别搞太多连接和 Channel,RabbitMQ 的 Web 受不了

RabbitMQ 的 Web 插件会收集很多连接,和其对应 Channel 的相关数据。

如果连接和 Channel 堆积太多了,整个 Web 打开会非常慢,几乎无法对 RabbitMQ 进行管理。所以,要注意限制连接和 Channel 的数量。

二、消息很宝贵,千万别乱抛弃哦

用来通信的消息是很宝贵的。

因为每条消息都可能携带了关键的数据和信息。所以,保证消息不丢失,需要根据消息的重要性,采取很多的措施。

1. 小心,Queue 存在再发消息

一条消息,在 RabbitMQ 中会先发到 Exchange,再由 Exchange 交给对应的 Queue。

而当 Queue 不存在,或者没匹配到合适的 Queue 的时候,默认就会把消息发到系统中的 /dev/null 中。

而且还不会报错。

这个坑当年把我坑惨了!我猜这个坑无数人踩过吧。

所以,在发送消息的时候,最好通过 declare passive 这种方法去探测下队列是否存在,保证消息发送不会丢的莫名其妙。

2. 收到消息请告诉我

在使用 RabbitMQ 客户端的时候,发送消息,一定要考虑使用 confirm 机制。

这个机制就是当消息收到了,RabbitMQ 会往客户端发送一个通知,客户端收到这个通知后,如果存在一个 confirm 处理器,那么就会回调这个处理器处理。这时候,我们就能确保消息是被中间件收到了。

所以,一定要考虑使用 confirm 处理器去确保消息被 RabbitMQ 服务器收到。

3. 有时候消息出了问题我也需要知道

在某些业务里,可能需要知道消息发送失败的场景,以便执行失败的处理逻辑。这时候,就要考虑 RabbitMQ 客户端的 return 机制。

这个机制就是当消息在服务器端路由的时候出现了错误,比如没有 Exchange、或者 RoutingKey 不存在,则 RabbitMQ 会返回一个响应给客户端。客户端收到后会回调 return 的处理器。这时候,客户端所在系统就能感知到这种错误了,从而进行对应的处理。

4. 为了一定不丢消息我也是拼了

还有的时候,消息需要处理强一致性这种事务性质的业务。这时候,就必须开启 RabbitMQ 的事务模式。但是,这个模式会导致整体 RabbitMQ 的性能下降 250 倍。

一般没有必要,不建议开启。

5. 把消息写到磁盘上

一般来说,为了防止消息丢失,需要在 RabbitMQ 服务器收到消息的时候,先持久化消息到磁盘上,防止服务器状态出现问题,消息丢失。

但是,持久化消息,必须先持久化队列,持久化队列完还不行,还必须把消息的 delivery mode 设置为 2,这样才能把消息存到磁盘。但是,这种行为会让整个 RabbitMQ 的性能下降 60%。

这种可以根据实际情况进行抉择。

三、对于收消息这件事,别由着性子来

1. 能一次拿多个干嘛要一次只拿一个

很多时候,一些 RabbitMQ 的新手,觉得如果在一个 mainloop 类似的无限循环里,去主动获取消息,会更加及时的获取到消息,也会拥有更加出色的性能。所以,他们会使用 get 这种行为去取代 consume 这种行为。

这时候,他们其实已经踩进了大坑。

为了能主动 get 服务器消息,很多新手会去写一个无限循环,然后不断尝试去 RabbitMQ 服务器端获取消息。但是,get 方法,其实是只去获取了队列中的第一条消息。

而采用 consume 方式呢,它的默认方式是只要有消息,就会批量的拿,直到拿光所有还没消费过的消息。

一个是一条条拿,一个是批量拿,哪个效率更高一目了然。

所以,尽量采用 consume 方式获取消息。

2. 拿消息也要讲方法论的

消费消息的时候,其实最难掌握的就是:

一次我们到底要取多少条消息?

对于 RabbitMQ 来讲,如果我们不对消费行为做限制,他会有多少消息就获取多少消息。这就造成了一个问题:

如果消息过多,我们一次性把消息读取到内存,很可能就会把应用的内存挤崩掉。

所以,我们要对这种情况做一些限制。

这时候,需要限制一次获取消息的数量,一般来讲,当我们的业务是异步发送,异步消费,不需要实时给回响应的时候,经验数据是一次获取 1000 条。

当然,系统和系统不一样,硬件条件也不一样,大家可以根据实际的情况来设置一次性获取的消息数量。

重点要说说同步。

在很多时候,我们需要通过 RabbitMQ 传送消息,并能通过临时队列等技巧去实时返回处理结果。这时候,就没办法一次抓多条数据进行处理了,因为,有发送端在等处理结果,依次处理,再依次返回,黄花菜都凉了。

而且大部分时候,这种同步等待响应的业务是有顺序要求的。所以,也不能并行同时抓出多条信息处理。那么,彼时,设置每次只消费一条消息就是理所应当的了。

最后

从上面的内容中,你也看到了,RabbitMQ 客户端如果要使用,对新手是多可恶的一件事情,各种坑,各种复杂性。

所以,如果你觉得 Spring 之类的 AMQP 客户端框架合你心意,那么你就使用它。

但是,Spring 的东西有个毛病,如果你要用它,你的应用必须也都要用 Spring。有些时候,也没有这种必要。这时候,你就可以根据我说的这些注意事项和经验,自己开发一套 RabbitMQ 的封装框架,去降低 RabbitMQ 的使用门槛。


你好,我是四猿外。

一家上市公司的技术总监,管理的技术团队一百余人。

我从一名非计算机专业的毕业生,转行到程序员,一路打拼,一路成长。

我会把自己的成长故事写成文章,把枯燥的技术文章写成故事。

欢迎关注我的公众号,关注之后还可以获取算法、高并发等干货学习资料。

我建了一个读者交流群,里面大部分是程序员,一起聊技术、工作、八卦。欢迎加我微信,拉你入群。

几年前,为什么我撸了一套RabbitMQ客户端?的更多相关文章

  1. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用

    通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...

  2. 【WebMisCentral WMC】基于Extjs 4.2x的企业级用户授权认证中心系统(SSO+AM+SM),多租户SAAS应用

    http://saas.chinacloudtech.com 题记 三年磨一剑,在企业信息化的道路上已经走了3年之久了,3年多时间里做了很多,突破了很多:有无奈和辛酸,也有收货与喜悦:自我价值也在不断 ...

  3. Nginx介绍

    原文:http://www.aosabook.org/en/nginx.html 作者: Andrew Alexeev nginx(发音"engine x")是俄罗斯软件工程师Ig ...

  4. iOS网络模块优化(失败重发、缓存请求有网发送)

    iOS开发中,一般都是通过AFN搭建一个简易的网络模块来进行与服务器的通信,这一模块要优化好没那么简单,需要花费很多时间与精力,仅仅根据这几年来的填坑经验,总结下这一块的需要注意的地方,也是给自己梳理 ...

  5. XX-NET史上最详细完整教程

     转 https://www.cnblogs.com/phperkang/p/8780123.html 前言 XX-NET,系GAE类代理,即通过可用Google ip连接Google App Eng ...

  6. utf-8和Unicode的区别

    链接 utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念 要想先讲清楚他们的区别,首先应该讲讲Unicode的来由. 众 ...

  7. nginx架构与基础概念

    1       Nginx架构 Nginx 高性能,与其架构有关. Nginx架构: nginx运行时,在unix系统中以daemon形式在后台运行,后台进程包含一个master进程和多个worker ...

  8. ascII、iso、utf-8、Unicode的区别

    utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念,utf-8是unicode的实现方式. 要想先讲清楚他们的区别,首先 ...

  9. ACM/OI 出题用

    之前出题,很苦恼出数据和检查程序,因为很多繁琐的工作,还很可能小手一抖出问题. 最近又在出题...想起之前的对拍脚本,感觉不能更方便,于是撸了一套出题用的小工具,也学习了一点点的DOS命令 首先是输入 ...

随机推荐

  1. 请求转发(forward)和请求包含(include)的区别?

    请求包含的例子 第一个Servlet (DispatcherServlet) @Override protected void doGet(HttpServletRequest req, HttpSe ...

  2. 关于Mapreduce Text类型赋值的错误

    Mapreduce中Text类型数据被无缘无故替换? ​ 今天偶然看到一个mapreduce demo,直接上手操作 统计两个文件中 最大值 文件中数据格式为 名字 数值 输出为 名字(最大值所对应的 ...

  3. ArrayList扩容机制以及底层实现

    简介 来源:博客园    作者:吾王彦 博客链接:https://www.cnblogs.com/qinjunlin/p/13724987.html ArrayList动态数组,是 java 中比较常 ...

  4. vagrant构建centos虚拟环境

    vagrant搭建centos 什么是vagrant 如何使用 1.构建本地的目录 2.官方下载对应的镜像文件,官方下载地址 3.导入刚刚下载的镜像(box文件) 4.初始化 5.修改Vagrantf ...

  5. php文件的自动加载

    <?php spl_autoload_register(function ($class_name) { require_once $class_name . '.php'; });

  6. spring boot 或 spring 集成 atomikos jta 完成多数据源事务管理

    前言:对于事务,spring 不提供自己的实现,只是定义了一个接口来供其他厂商实现,具体些的请看我的这篇文章: https://www.cnblogs.com/qiaoyutao/p/11289996 ...

  7. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  8. POJ1703带权并查集(距离或者异或)

    题意:       有两个黑社会帮派,有n个人,他们肯定属于两个帮派中的一个,然后有两种操作 1 D a b 给出a b 两个人不属于同一个帮派 2 A a b 问a b 两个人关系 输出 同一个帮派 ...

  9. Winrar漏洞复现(CVE-2018-20250)

    本文讲的是Winrar漏洞利用脚本的使用方法,至于Winrar漏洞的原理,请移步--> Winrar目录穿越漏洞复现 本次利用脚本出处--> https://github.com/back ...

  10. Photoshop 第二课 工具-钢笔的使用

    钢笔的使用 钢笔→ 是一个非常实用(主要用于)但是非常难操作(会者不难哦~)的工具. 钢笔属性中有三种状态:1.路径:2.形状:3.像素.其中路径和形状是我们最常用的状态.路径是一条用来圈定需要操作的 ...