spring-rabbit 中的 ClassNotFoundException 取决于何时启动消费者或生产者
ClassNotFoundException in spring-rabbit depending on when consumer or producer is started
我正在使用 Spring 使用 spring-amqp 和 RabbitMQ 引导在我在本地 运行 的两个 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[=14=]1(SimpleMessageListenerContainer.java:93)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.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.access00(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.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 类:
服务器配置:
@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);
}
}
我最初发现 具有几乎相同的堆栈跟踪,但这种情况下的解决方案是将所有常见事件/模型放在一个项目中,并将该项目包含在服务器和客户端中项目。但是,我已经在这样做了。我还尝试使用 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
。
以下是我正在使用的相关依赖项:
- Spring Boot v1.3.8.RELEASE
- Spring AMQP v1.5.6.RELEASE
- Spring 兔子 v1.5.6.RELEASE
这很可能是某种 Classloader
问题 - 也许您在 class 路径上有两个版本的 class。
我发现调试此类问题的最简单方法是 运行 带有 -verbose
的 JVM 并监视从何处加载 class。
比较有效的 运行 和无效的日志。
我对你遇到与 JSON 相同的问题并不感到惊讶,因为完全限定的 class 名称是在 header.
中传递的
此外,您的 jars 中是否有独特的包?如果您从不同的罐子中提供相同的包,您可能会遇到这样的问题。
我正在使用 Spring 使用 spring-amqp 和 RabbitMQ 引导在我在本地 运行 的两个 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[=14=]1(SimpleMessageListenerContainer.java:93)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.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.access00(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.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 类:
服务器配置:
@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);
}
}
我最初发现
@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
。
以下是我正在使用的相关依赖项:
- Spring Boot v1.3.8.RELEASE
- Spring AMQP v1.5.6.RELEASE
- Spring 兔子 v1.5.6.RELEASE
这很可能是某种 Classloader
问题 - 也许您在 class 路径上有两个版本的 class。
我发现调试此类问题的最简单方法是 运行 带有 -verbose
的 JVM 并监视从何处加载 class。
比较有效的 运行 和无效的日志。
我对你遇到与 JSON 相同的问题并不感到惊讶,因为完全限定的 class 名称是在 header.
中传递的此外,您的 jars 中是否有独特的包?如果您从不同的罐子中提供相同的包,您可能会遇到这样的问题。