天天看點

記第一次使用Jstack完成Bug排查的過程

1、項目結構:後端程式 ---- kafka ---- 中間件 ---- 終端裝置

該項目為資料采集項目,即由終端裝置上傳資訊到中間件,中間件将資料上傳到kafka,再被後端業務程式讀取。由于終端上傳的資料有多個種類,是以後端程式采用多線程的方式處理,每一個線程處理一個類别的資料。

2、背景:

在終端裝置保持資料上傳的情況下,後端程式正常性出現讀取不到資料的情況。重新開機後端服務後可恢複讀取,是以判斷為後端程式出現異常。

3、排查過程:

a、檢視後端程式日志,發現無異常日志。

b、檢視用來讀取資料的線程Thread-14,發現該線程在某個時間點之後再無日志輸出。由此懷疑是該線程出現問題,但仍然找不到異常輸出。

c、使用jstack檢視線程狀态,發現Thread-14的狀态為waiting,即無限期地等待喚醒。由jstack輸出的資訊可以知道,問題出現在ArrayBlockingQueue.put方法上。我們知道ArrayBlockingQueue在存滿資料的情況下會阻塞住線程,知道資料被取出才會被重新喚醒。這裡基本把問題定位出來了。

"Thread-14" #71 prio=5 os_prio=0 tid=0x00007f03f2883000 nid=0x4286 waiting on condition [0x00007f0451ae5000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000006cbcbd248> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)
	at cn.ffcs.semi4.msa.data.acquisition.common.ConsumerRecordPool.putCanInfoReissueConsumerRecordPool(ConsumerRecordPool.java:149)
	at cn.ffcs.semi4.msa.data.acquisition.kafka.consumer.KafkaUtil.startConsumer(KafkaUtil.java:162)
	at cn.ffcs.semi4.msa.data.acquisition.message.impl.SubscribeTopicProcessor$ConsumerHandle.run(SubscribeTopicProcessor.java:70)
           

d、檢視後端程式日志,發現有個隊列确實存滿。從設計上來看,隻要資料存進來就會被讀取,是以堆滿整個隊列的情況是不合理的。

e、檢視讀取改列隊的線程,發現有異常資料未被catch,導緻線程死亡。(jstack隻能檢視目前存活的線程狀态,死亡的線程是看不到的)

f、加上異常捕獲的語句,問題解決。