• 解决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 }
  • 相关阅读:
    0802作业1替换文本文件内容

    看病
    爬山
    作业1
    超市(未完成)
    图片复制
    替换
    文件
    英文字母和中文汉字在不同字符集编码下的字节数
  • 原文地址:https://www.cnblogs.com/buguge/p/14273332.html
Copyright © 2020-2023  润新知