之前文章说过,如果使用 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. 扩展 GRTN:云原生趋势下的 RTC 架构演进

    在 2021 LiveVideoStackCon 音视频技术大会上海站,聚焦 "轻端重云和边缘架构新模式" 专场,阿里云视频云的 RTC 传输专家杨成立(忘篱)带来 "基 ...

  2. 【Mybatis源码解析】- JDBC连接数据库的原理和操作

    JDBC连接数据库的原理和操作 JDBC即Java DataBase Connectivity,java数据库连接:JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句, ...

  3. JDK8新特性(一) Lambda表达式及相关特性

    函数式接口 函数式接口是1.8中的新特性,他不属于新语法,更像是一种规范 面向对象接口复习 在这里先回顾一下面向对象的接口,创建接口的关键字为interface,这里创建一个日志接口: public ...

  4. 已知a=a

    高中时酷爱经济学. 薄薄的纸片竟然决定着整个社会的运转趋势,整个人生的起伏也是靠着纸片来衡量的. 可笑的是你怎么闹腾也逃不过康波周期等一系列命中注定的路线,即,已知a=a,那么a等于且仅等于a. 所有 ...

  5. jQuery+AJAX实现纯js分页功能

    使用jQuery的AJAX技术,在bootstrap的框架下搭建的纯js分页 bootstrap作为Twitter推的一款前端框架,效果个人还是觉得很不错的.这次只是拿来作为网页元素的css样式表使用 ...

  6. Thinkphp5之Bootstrap自定义BootstrapDetailed分页插件的实现

    首先,在此目录建立一个newcrm\thinkphp\library\think\paginator\driver\BootstrapDetailed.php文件,代码如下: <?php // ...

  7. ajax异步登录

    以下是ajax代码示例: HTML <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  8. 从苏宁电器到卡巴斯基第29篇:难忘的三年硕士时光 VII

    我们可能无家可归 那天晚上和导师道别后,我们几个还聚在一起开了一个小会.当时大家觉得最坏的情况就是学院不肯让步,不能满足我们导师提出的条件.那么这样的话,我们几个只能够重新找导师了.而我们数媒专业里面 ...

  9. POJ 1716 区间最小点个数

    题意:      给你n个区间,每个区间最少取两个元素,问你所有区间最少取几个元素(可以满足每个区间最少两个元素). 思路:      这个题目感觉挺巧妙的,之前在杭电上做过这个题目,这个题目可以用查 ...

  10. python中让输出不换行

    Python2.x python2.x中输出默认是换行的,为了抑制换行,可以在打印最后加一个逗号 Python3.x 到了python3中,print 变成一个函数,这种语法便行不通了. 我们可以使用 ...