线上消息堆积与感想
共 2449字,需浏览 5分钟
·
2021-04-28 02:29
环境介绍
1.应用服务器部署在阿里云
2.消息中间件使用阿里云RocketMQ
前两天线上发生了MQ消息堆积的情况,在我的知识认知里,消息堆积的原因是消费者消费能力差,无法及时消费消息,才会导致消息堆积.
那么有哪些因素会导致消费者消费能力差呢? 我目前的理解有三种情况
因素
1:Dubbo的RPC调用时间太久
2:查询数据库或者插入数据太久
3:业务中还有其他耗时操作,比如发送邮件
记得以前看博客的时候,似乎看到过说发送邮件耗时久导致消息堆积的情况, 而现在就发生在自己身上,真是冥冥之中自有安排.
发生消息堆积,查看阿里云上的线程堆栈信息,如下
从线程堆栈上来看,线程在发送邮件,准确说在连接. 根据代码分析以及一段时间的观察线程,断定就是发送邮件的逻辑导致的.临时注释掉这段代码,重新发布,问题解决.
其实发送邮件并不会耗时太久, 而根据阿里云ARMS上的错误显示,消息消费失败总耗时2min. 为什么发送邮件(准确说是连接)会这么久. 因为那段发送邮件的代码在本地测试,只耗时1s左右.
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public static void main(String[] args) {
long start = System.currentTimeMillis();
try {
// 配置仅为了文章说明,并不是真实配置
Mail mail = new Mail("xyz@qq.com", "password");
mail.addToAddress(Mail.ReceiveType.TO, "opq@qq.com");
mail.setMailInfo(Mail.Mode.TEXT_MAIL, "这是测试数据", "这是测试数据");
mail.send();
} catch (Throwable e) {
System.out.println(e.getMessage());
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
我把上面的代码在线上机器测试,发现耗时的确在2min. 为了描述, 我这里以我自己的阿里云服务器做演示.
测试了两次,耗时都在1min.
接下来通过strace命令查看系统调用, 看下具体耗时在哪个系统调用上.
关于如何使用strace命令查看系统调用,在我之前的文章中也有过一些介绍
邮件发送默认使用25号端口,搜索下关键字25
grep -iE 'connect.*25' strace*
在连接邮件服务器25号端口的时候,超时了.
为什么本地耗时1s, 而在阿里云上的机器就耗时这么久呢? 原因只要一个
阿里云默认禁用了25号端口,所以导致连接超时.
在本次事件中,还发现一个情况
消息已经消费成功,可是MQ Broker依然投递消息. 简单咨询了阿里云客服,也没给我个具体原因.
●常见消费者消费慢的因素有哪些?
●消费慢的时候,要查看下线程堆栈,观察线程都在干啥
●你会使用strace命令查看系统调用吗?
如上图,目前给我的感觉,作为一个Java码农或者工程师,系统上应该要对以上四个都要熟悉.
你不仅要会使用Java层面的Map,Thread,AQS等等, 当它调用到native方法时候,你还要去JVM层面追踪查看具体的实现,JVM层面可是C或者C++语言. 还有它最终调用到的系统调用是哪个. 而绝大多数人都停留在Java层面,当然这个层面达到精通也很难. 了解JVM的更少了, 能够继续向下了解C和Linux的那就凤毛麟角了.
你掌握的技术,一个刚毕业的学生在2个月或者半年就掌握了,但是别人掌握的知识,你未必在1-2年能掌握. 这或许就是人与人之间的区别. 你是哪种人呢?