解決ActiveMQ隊列消費時提示JMSException:ClassNotFoundException


需求開發完成提測,測試時發現activemq的listener在消費隊列消息時,程序捕獲到異常。見下面log。
異常信息很明顯,com.cn.yft.ora.entity.TAccReviewRecord這個class不存在。一看這個ClassNotFoundException異常,我聯想到以前用redis存儲熱數據的時候也出現過。

2021-01-12 20:14:14,734 [ERROR] [ListenCommonSyncMQService-1] [com.yft.busi.mq.CommonSyncMQService:40] [隊列發送]同步落地服務公司充值信息發送失敗
javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: com.cn.yft.ora.entity.TAccReviewRecord
    at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:36)
    at org.apache.activemq.command.ActiveMQObjectMessage.getObject(ActiveMQObjectMessage.java:193)
    at com.yft.busi.mq.CommonSyncMQService.onMessage(CommonSyncMQService.java:31)
    at org.springframework.jms.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:341)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:537)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:497)
    at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:468)
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:325)
    at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:263)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1102)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1094)
    at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:991)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: com.cn.yft.ora.entity.TAccReviewRecord
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1928)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1771)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.apache.activemq.util.ClassLoadingAwareObjectInputStream.load(ClassLoadingAwareObjectInputStream.java:95)
    at org.apache.activemq.util.ClassLoadingAwareObjectInputStream.resolveClass(ClassLoadingAwareObjectInputStream.java:43)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1868)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at org.apache.activemq.command.ActiveMQObjectMessage.getObject(ActiveMQObjectMessage.java:191)
    ... 11 more

 

為什么會出現這樣的異常呢?
這通常發生在系統實體類調整的時候,或,系統程序結構發生變化的時候。

 

先說本案,原因正是后者。原先呢,指定的ActiveMQ隊列的消息生產者及其消費者是在相同的一個應用服務里。生產者放入隊列的消息是TAccReviewRecord對象,那么,顯然,同一應用里的消費者MessageListener監聽到消息后,獲取message攜帶的數據依然是程序里的TAccReviewRecord對象。

而之所以出現了開篇提到的ClassNotFoundException異常,是因為在開發新需求時,另一個應用服務也要為指定ActiveMQ隊列生產消息。另一個服務的TAccReviewRecord的package與原先服務里TAccReviewRecord的package不同,從而導致了上文的ClassNotFoundException異常。

 

再來說說之前redis存取熱數據遇到的這個異常,其原因是前者。 情況是設置緩存調用了redis.set(String key, Object value)方法,即value直接指定的VO對象。后來,程序調整了目錄結構,那個VO對象的package發生了改變。那么,程序重新發布到服務器上,當對該key的舊有的未失效緩存值做類型轉換時,就出現了這個ClassNotFoundException。

 

【解決辦法】
為了規避類似問題,經過評估,采用了犧牲性能保證可用的策略,即,不再直接存放數據對象,而是將數據對象序列化為json串,讀取的時候同樣也做反序列化。


【附】改造前出現ClassNotFoundException異常的代碼
activemq producer代碼(應用A)

public int sendToQueue(String conFactory,String userName, String pwd, String tCPUrl,
        final Object message, String qMName) {
    。。。
    Message testmessage = session.createObjectMessage((Serializable) message);
    // 發送消息到目的地方
    producer.send(testmessage);
    session.commit();
    。。。
}

activemq consumer代碼(應用B) ----異常就是在第10行objectMessage.getObject()拋出來的

 1 @Component("commonSyncMQService")
 2 public class CommonSyncMQService implements MessageListener {
 3     private final static Logger logger = LoggerFactory.getLogger(CommonSyncMQService.class);
 4 
 5     @Override
 6     public void onMessage(Message message) {
 7         if (message instanceof ObjectMessage) {
 8             final ObjectMessage objectMessage = (ObjectMessage) message;
 9             try {
10                 logger.info("同步落地服務公司充值信息接口消息隊列接收參數[{}]",objectMessage.getObject());
11                 TAccReviewRecord reviewRecord = (TAccReviewRecord) objectMessage.getObject();
12                 。。。
13             } catch (final Exception e) {
14                 logger.error("[隊列發送]同步落地服務公司充值信息發送失敗", e);
15             }
16         }
17     }
18 }

 

【附】改造后使用序列化字符串取而代之的代碼

activemq producer(應用A)存放json串后,activemq consumer(應用B) 改造后:

 1 @Component("commonSyncMQService")
 2 public class CommonSyncMQService implements MessageListener {
 3     private final static Logger logger = LoggerFactory.getLogger(CommonSyncMQService.class);
 4     @Autowired
 5     private SyncAccReviewRecordService syncAccReviewRecordService;
 6 
 7     @Override
 8     public void onMessage(Message message) {
 9         if (message instanceof ObjectMessage) {
10             final ObjectMessage objectMessage = (ObjectMessage) message;
11             try {
12                 logger.info("同步落地服務公司充值信息接口消息隊列接收參數[{}]",objectMessage.getObject());
13                 TAccReviewRecord reviewRecord = JSONObject.parseObject(String.valueOf(objectMessage.getObject()),TAccReviewRecord.class);
14                 。。。
15             } catch (final Exception e) {
16                 logger.error("[隊列發送]同步落地服務公司充值信息發送失敗", e);
17             }
18         }
19     }
20 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM