Bootstrap

贞炸了!上线之后,消息收不到了!

hello,各位小伙伴们,上午好~

昨晚生产系统机房切换,又度过了一个不眠之夜。趁着这段无聊时间,分享一下前一段时间 RocketMQ 踩坑经历

欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:

前言

事情是这样的,前端时间我们有个新业务上线,这个业务需要监听支付成功的 mq 消息,然后向绑定的音箱推送消息。这样用户在支付完成之后,商家端就就可以收到收款播报。

起初我们在测试环境的测试的时候,一切流程非常顺利,没有任何问题。但是等到我们发布上线之后,却出现了问题。

一笔支付成功之后,音箱没有发出收款成功的播报。一切流程排查下来之后,这才发现原来 MQ 消费端没有正常在消费消息。

开始排查问题,第一想到的是消费端是不是发布失败了,但是查看相关日志,并没有任何异常。

登录 MQ 控制台,尝试手动重新发布消息,神奇的事来了,消费端成功收到消息

总结现在的问题,下文开始排查。

排查问题

刚开始排查的时候,由于没有任何异常业务日志可以定位问题,所以问题排查起来十分困难。

排查了两天了,想过各种问题。比如当前消费端使用 RocketMQ 客户端版本比较高,是不是版本兼容性导致的问题呢?

于是降低消费端的版本,重新发布之后,问题依然存在。

没办法,只好使用 Google 大法了。

通过搜索发现,原来默认情况下 rockmq 客户端的日志将会单独打印输出,日志文件位置如下:

${user.home}/logs/rocketmqlogs

下图为当时的日志截图:

可以看到消费端尝试连接一个 20878 的端口,但是由于网络问题,一直连接失败。

那这个 20878 是什么端口?

我们并没有主动配置这个端口,但是 rocketmq broker 配置的端口为 20880。

搜索发现,原来 rocketmq broker 默认将会启动三个通讯端口:

第一个是 rocketmq broker 配置文件上配置的端口,默认端口为 10911,这里我们修改成了 20880。

第二个是 rockemq broker vip 通道端口,这个端口将会在第一个端口基础上减 2,即 20878。

第三个是 rockemq broker 用户主从数据同步的端口,这个端口将会在第一个端口基础上加 1,即 20881。

大概知道问题,解决办法就很简单了,要么防火墙打开 29878 网络端口的限制,要么关闭使用 vip 端口。

RocketMQ 客户端提供两种方式关闭使用 vip 端口。

## 消费端
DefaultMQPushConsumer#setVipChannelEnabled(false)
## 生产端
DefaultMQProducer#setVipChannelEnabled(false);

-Dcom.rocketmq.sendMessageWithVIPChannel=false

源码分析

虽然问题解决了,但是上述问题本质原因还没有找到。所以这次我们就从源码出发,追本溯源。

为什么 vip 端口网络不通将会导致消费者不能正常消费?

从 rocketmq 错误日志,我们可以看到报错代码位于 类中。

这里主要用来执行 topic Rebalance(重平衡)。

首先我们来了解一下, 目的是为什么了。

假设当前 rocketmq broker 端存在一个 topic ,拥有四个队列,关系如下:

此时如果有一个消费者使用集群模式消费消息,那么它将需要负责消费所有队列中的消息。

当我们再增加一个消费者消费消息时,此时消费端将会自动进行重平衡,默认情况下将会使用平均分配原则。

可以看到 机制可以提升的消息的并行处理机制。

rocketmq 消费端启动时竟会触发 机制。接着,我们根据源码主要看下 主流程,代码位于。

通常我们使用集群消费模式,所以这里主要看集群模式下 过程。

上述代码整体流程如下:

后续消息流程就不看源码,比较复杂,网上找了一张消息消费流程图:

可以看到,由于网络端口问题,无法正常获取所有消费者 ID 集合,这就导致无法正常分配队列信息。

 List cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);

由于未被分配任一队列,消费端程序也就业务无法正常拉取消息。

为什么 mq 控制台重新发送的消息消费者可以收到?

rocketmq 控制台重新发送消息代码如下:

将会把消息的元数据封装一个类型的请求,接着调用 rocketmq 提供的 admin API,给 rocketmq broker 发送请求。

broker 端收到请求之后,将会查询消息,然后再向消费端发起 请求。消费端接受到消息请求之后,将会直接消息这条消息。

为什么 broker 将会启动两个端口?

rocketmq broker 虽然启动了两个端口,但是从 rocketmq broker 的源码可以发现这两个端口启动之后起到作用是一样的。

那为什么开启两个监听端口那?我想很多同学应该也有这个疑惑,这里给出一个开发者解释答案。

https://github.com/apache/rocketmq/issues/1510

普通的端口将会承载所有消息网络请求,如果此时请求非常繁忙,broker 端的所有 I/O 线程可能都在执行请求,这就会导致后续网络请求进入队列,从而导致消息请求执行缓慢。

这对于生产者来说,可能是一个致命的问题,因为消息生产者通常消息发送延时要低。

这种情况下,我们就可以将消息发送到 VIP 端口,从而降低消息发送的延时。

默认情况下,rocketmq 客户端的 配置为 。

private boolean vipChannelEnabled = Boolean.parseBoolean(System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true"));

生产者的发送消息,消费者获取元数据信息等请求默认将会使用 端口。

**不过这里需要注意一点,消费者拉取消息,将不会使用 端口。**

虽然这个设计很巧妙,但是说实话个人觉得这个配置权限应该交给开发者自己去配置,而不是默认开启。

因为不熟悉的情况下还是很容易踩坑的,默认情况下,大家应该只熟悉 9876 与 10911 这两个端口。

**rocketmq 4.5.1 版本之后, 配置被修改为 ,这时是否使用 vip 端口真正交给开发者自己**

如果此时想开启,需要主动 API 参数,或者 JVM 参数增加

总结

今天的问题主要由于 VIP 端口无法连接,从而导致消费端无法正常消费消息。虽然最后的解决办法非常简单,但是这个排查过程真的很难。

我们平常在使用 rocketmq 过程中,通常只要设置 nameserver 的配置即可, broker 等地址信息将会自动从 nameserver 获取。这就间接导致了,我们可能只了解 9876 这个端口。

生产环境由于网络安全问题,一般不会开放全部的端口。所以,我们在使用 rocketmq 的过程,需要了解以下四个端口,分别为(默认配置):

  • 9876:nameserver 监听端口

  • 10911: broker 监听端口

  • 10909:broker vip 监听端口

  • 10912:broker HA 端口,用于主从同步

生产使用 rocketmq 过程,如果碰到诡问题,不妨尝试 telnet 看下网关连通性。另外还可以通过查看 rocketmq 自身日志,确定问题,日志位置位于:

${user.home}/logs/rocketmqlogs

好了,今天文章就到这里。我是楼下小黑哥,你知道的越多,你不知道的就越多。

下周见~

欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客: