事情起因:前些天项目经理给派了一个任务:将三个老系统(JSP)的短信接口升级,对接到第三方公司新的MQ短信接口。
处理过程:短信发送逻辑:从数据库中查询`需要发送的短信`->发送消息到第三方MQ->由第三方发送短信。发短信这个业务不同场景有着不同的发送频率,三个系统:其中一个带有验证码功能,这个需要一分钟发送一次,其他俩个只是业务短信所以并没有设置太频繁,设置了三分钟一次。看一了一下第三方的RocketMQ使用DEMO,比较简单,启动生产者-》发送指定tag的json到对应的topic就可以了。当时没想太多,直接做了一些简单的修改就放进了我自定义的component中。这里放一下MQ中的代码片段(这个片段就是我查产生问题的地方)
中间的发送逻辑就不展示了,打了个Jar包,就部署到线上服务器了,第二天,发现内存free(VIRT物理内存15.1G,虚拟内存6.5G,共享内存都有105532kb)被我这个短信服务吃掉不少,而且占用CPU的时间达到了177小时,紧急停止排查问题。查看error日志,发现报了java.lang.OutOfMemoryError: unable to create new native thread这个错误。
当时我一看到无法创建线程,就觉得是不是需要线程池管理一下,又添加了定时任务的线程池,发现这个报错不见了,但是内存问题还没有解决。(其实第一次的解决思路完全是错误的,正常来说定时任务执行完,线程就无需使用了,不用创建那麽多新线程),我又想是不是哪些哪些资源没有被释放导致,系统一直占用内存资源呢,使用java自带的JStack命令查看了一下这个应用程序,发现大部分线程都处在wait或者timewait的状态,这就有问题了,但是还是无法确认是什么导致了线程都处在这个状态,这时候就需要借助工具了。(其实这里面使用jstack -l pid > jstack.txt导出内存情况也可以发现RequestHouseKeepingService这个东西一直在占用,后来才知道`JStack`和其他JAVA命令应该好好学习一下,有助于排查问题)
部署了Arthas使用thread --state查看线程信息,发现RequestHouseKeepingService都处在wait状态,数量很多。
经过查阅资料,发现这个线程在是由生产者创建的(DefaultMQProducerImpl这个类下),所以推测可能是因为MQ生产者创建不当导致的。
MQ的生产者不应该一直重复创建,创建完后一直使用一个就可以,所以改为了注入的方式(单例),创建MQ生产者(因为以前没用过RocketMQ,但是用过rabbitMQ和kafka,其实仔细一想这种东西-工具确实就应该创建一次就可以,而且不用关闭,写代码不经大脑直接粘贴确实挺蠢,多思考),这个.start方法有疑问,有的文章说不使用它,但是我测试果会报一个MQClientExceptionThe producer service state not OK, maybe started once, RUNNING的错误,使用了就没问题了。(可能是版本问题?)
总结:基础不够扎实,线程知识,代码常识需要加强。学习学习