背景:公司产品需要做微信服务,了解的人都知道微信粉丝有交互就会给第三方平台推送事件消息。也就是说产品管理的公众号越多,公众号的粉丝越多,那么平台需要处理的事件消息也就会越频繁。于是想到用MQ解决压力问题。自己搭建MQ维护成本又高,自然而然的想到阿里云。
具体的详细介绍,官方帮助都有说明:
https://help.aliyun.com/document_detail/29532.html?spm=5176.doc29532.6.539.rompTV
接入方式有三种:TCP、HTTP、MQTT。考虑到实时性,我采用的是TCP普通消息接入。
然后创建 Topic 创建、Producer ID、创建 Consumer ID、创建阿里云 AccessKey,SecretKey。
接下来就是按照DEMO引入JAR包,进行开发,测试,上线。(没有很好的测试条件,没有进行压测)
上线后发现过一段时间服务器CPU使用率就会暴增,直至无响应。重启后情况依旧,郁闷了。。。。。。
经过查看堆栈等一系列跟踪排错,发现有很多thread在进行空等待,类似于下面的信息
"ConsumeMessageThread_13" daemon prio=10 tid=0x00007ff57c018000 nid=0x1fdf waiting on condition [0x00007ff52d8d7000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007035c4f30> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
利用Jmeter进行模拟发现,每一个消息生产、消费的过程,都要产生10多个thread,线程数在无限增长,JVM也不进行回收,直至CPU无响应。
产生原因:由于轻信了DEMO中的producer的shutdown()方法
// 在应用退出前,销毁Producer对象<br>
// 注意:如果不销毁也没有问题
producer.shutdown();
所以每当生产一个消息的时候,我就会新创建一个producer实例。而每个producer实例又会产生多个thread,数量在5-20左右(取决于自己的配置情况)。虽然调用了shutdown()方法,但是实际上,producer实例和其产生的thread并没有回收掉,thread都将进入上面描述的空等待状态。
解决方法:其实很简单,在创建Producer实例发送消息的时候,使用单例模式。(撞墙,万万没想到啊)