2021年1月13日星期三

解决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对象。
之所以出现了这个异常,是因为在开发新需求时,消息生产者是在另一个服务。另一个服务的TAccReviewRecord的package与原先服务里TAccReviewRecord的package不同,从而导致了上文的ClassNotFoundException异常。

再说说之前redis存取热数据遇到的这个异常,其原因是前者。 情况是设置缓存调用了redis.set(String key, Object value)方法,即value直接指定的VO对象。后来,程序调整了VO的目录结构。那么,重新发布到服务器上,再读取指定key的值时,就出现了这个ClassNotFoundException。

 

【解决办法】
为了规避类似问题,经过评估,采用了牺牲性能保证可用的策略,即,不再直接存放数据对象,而是将数据对象序列化为json串,读取的时候同样也做反序列化。


【附】
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 }

 









原文转载:http://www.shaoqun.com/a/508726.html

跨境电商:https://www.ikjzd.com/

adore:https://www.ikjzd.com/w/2202

笨鸟转运:https://www.ikjzd.com/w/1550


需求开发完成提测,测试时发现activemq的listener在消费队列消息时,程序捕获到异常。见下面log。异常信息很明显,com.cn.yft.ora.entity.TAccReviewRecord这个class不存在。一看这个ClassNotFoundException异常,我联想到以前用redis存储热数据的时候也出现过。2021-01-1220:14:14,734[ERROR][List
手机trademanager:手机trademanager
inkfrog:inkfrog
欢乐谷120个免费游园大放送:欢乐谷120个免费游园大放送
【去荷兰旅游一周的预算】--去荷兰旅游一周需要多少钱:【去荷兰旅游一周的预算】--去荷兰旅游一周需要多少钱
皇岗汽车站订票电话是多少?:皇岗汽车站订票电话是多少?

没有评论:

发表评论