我正在使用带有spring-amqp和RabbitMQ的Spring Boot在本地运行的两个JVM之间发送消息。根据我启动每个应用的顺序,我有时会得到ClassNotFoundException
。我有一个像这样的多项目设置:
- Project root
- common (contains all events / messages that are sent)
- server
- client
首先启动服务器时,它会等待来自客户端的消息。当客户端启动时,它会实现ApplicationListener<ApplicationReadyEvent>
并向服务器发送一条消息,表明它已准备就绪。
服务器监听器:
@Component
@RabbitListener(queues = "server.${server.id}")
public class ServerListener {
private static final Logger logger = LoggerFactory.getLogger(ServerListener.class);
@RabbitHandler
public void onMessageReceived(@Payload ClientAvailableEvent event) {
logger.info("Server: Received request from client ID = {}", event.getClientId());
}
}
客户制作人:
@Component
public class ClientReadyProducer implements ApplicationListener<ApplicationReadyEvent> {
private static final Logger logger = LoggerFactory.getLogger(ClientReadyProducer.class);
@Value("${client.id}")
private String id;
private final RabbitTemplate template;
@Autowired
public EventBasedModuleRegistration(RabbitTemplate template) {
this.template = template;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
logger.info("Client initialized.");
ClientAvailableEvent event = ClientAvailableEvent.from(id);
template.convertSendAndReceive("server.exchange.all", "", event);
}
}
当服务器收到此消息时,日志会以无限数量的堆栈跟踪爆炸,抱怨无法找到ClientAvailableEvent
:
2017-01-30 09:30:22.610 WARN 63573 --- [cTaskExecutor-1] s.a.r.l.ConditionalRejectingErrorHandler : [][] Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:865)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:760)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:680)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:93)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:183)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1358)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:661)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1102)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1086)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1100(SimpleMessageListenerContainer.java:93)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1203)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: Could not deserialize object type
at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:82)
at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:110)
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:185)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:173)
at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:118)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:102)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:88)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:757)
... 10 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.example.event.ClientAvailableEvent
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:250)
at org.springframework.core.ConfigurableObjectInputStream.resolveClass(ConfigurableObjectInputStream.java:74)
at org.springframework.amqp.support.converter.SimpleMessageConverter$1.resolveClass(SimpleMessageConverter.java:179)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1613)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at java.util.ArrayList.readObject(ArrayList.java:791)
at sun.reflect.GeneratedMethodAccessor46.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1900)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2000)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1924)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:76)
... 17 common frames omitted
然而,我可以让这个例外消失。在客户端仍然运行的情况下,如果我重新启动服务器,一切都很好,并继续工作没有问题。我可以重新启动客户端,它将发送另一个ClientAvailableEvent
,服务器将很乐意反序列化它。
以下是我的Spring课程:
ServerConfiguration:
@Configuration
@EnableRabbit
public class ServerConfiguration {
@Value("${server.id}")
public String id;
@Bean
public Queue serverQueue() {
return new Queue("server." + id, false, true, true);
}
@Bean
public TopicExchange serverExchange() {
return new TopicExchange("server.exchange");
}
@Bean
public Binding bindingById() {
return BindingBuilder.bind(serverQueue()).to(serverExchange()).with(id);
}
@Bean
public FanoutExchange allServersExchange() {
return new FanoutExchange("server.exchange.all");
}
@Bean
public Binding bindingToAll() {
return BindingBuilder.bind(serverQueue()).to(allServersExchange());
}
@Bean
public TopicExchange clientExchange() {
return new TopicExchange("client.exchange");
}
@Bean
public RabbitAdmin amqpAdmin(ConnectionFactory factory) {
return new RabbitAdmin(factory);
}
}
客户端配置:
@Configuration
@EnableRabbit
public class ClientConfiguration {
@Value("${client.id}")
private String id;
@Bean
public Queue clientQueue() {
return new Queue("client." + id, false, true, true);
}
@Bean
public TopicExchange clientExchange() {
return new TopicExchange("client.exchange");
}
@Bean
public Binding bindingById() {
return BindingBuilder.bind(clientQueue()).to(clientExchange()).with(id);
}
@Bean
public TopicExchange clientExchange() {
return new TopicExchange("client.exchange");
}
@Bean
public FanoutExchange allClientsExchange() {
return new FanoutExchange("client.exchange.all");
}
@Bean
public Binding bindingToAll() {
return BindingBuilder.bind(clientQueue()).to(allClientsExchange());
}
@Bean
public RabbitAdmin amqpAdmin(ConnectionFactory factory) {
return new RabbitAdmin(factory);
}
}
我最初发现this question具有几乎相同的堆栈跟踪,但在这种情况下,解决方案是将所有常见事件/模型放在一个项目中,并将该项目包含在服务器和客户端项目中。但是,我已经这样做了。我还尝试使用JSON发送消息(通过在两个配置中添加以下内容)而不是标准序列化:
@Bean
public MessageConverter producerJsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
@Bean
public MappingJackson2MessageConverter consumerJsonMessageConverter(){
return new MappingJackson2MessageConverter();
}
@Bean
public DefaultMessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(consumerJsonMessageConverter());
return factory;
}
@Bean
public RabbitTemplate configureRabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(producerJsonMessageConverter());
return template;
}
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(producerJsonMessageConverter());
return factory;
}
使用JSON导致类似的堆栈跟踪抱怨ClassNotFoundException
。
以下是我正在使用的相关依赖项:
答案 0 :(得分:1)
这很可能是某种Classloader
问题 - 也许你在类路径上有某种类的两个版本。
我发现调试此类问题的最简单方法是使用-verbose
运行JVM并监视加载类的位置。
比较运行的运行和不运行的运行之间的日志。
我对JSON遇到同样的问题并不感到惊讶,因为在标题中传递了完全限定的类名。
另外,你的罐子里有独特的包装吗?如果您从不同的罐子中提供相同的包装,您可能会遇到类似的问题。