我使用spring-spring和spring-amqp和RabbitMQ在本地運行的兩個JVM之間發送消息。根據我開始每個應用的順序,我有時會得到一個ClassNotFoundException
。我有一個像這樣的多項目設置:Spring-rabbit中的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
。
下面是我使用的相關依存關係:
- 春季啓動v1.3.8.RELEASE
- 春AMQP v1.5.6.RELEASE
- 春兔v1.5.6.RELEASE
當它工作時,'-verbose'告訴我在我的工作區的Gradle構建輸出中的JAR中找到了該類:[從文件加載com.example.event.ClientAvailableEvent:/ Users/nick/dev/tmp /spring-amqp-project/common/build/libs/common-0.0.1-SNAPSHOT.jar。這對我來說似乎是合乎邏輯的,因爲班級也在我的工作空間中,但我不明白爲什麼在第一次運行時找不到它。當我引導運行客戶端時,可能Gradle覆蓋它,因爲它們在同一個項目中,或者類似的事情發生在JAR上。 – nickb
這可能是它的原因 - 嘗試從引導超級jar而不是'bootRun'運行,'Spring Boot Gradle插件還包含一個bootRun任務,它可以用來以爆炸形式運行你的應用程序。 –
從超級JAR運行很好。終於找到它了。我的Gradle構建中有一項任務在':common:jar'之前運行,導致JAR任務始終過時,因此每個構建都會導致重建common.jar。無論出於何種原因,一旦JAR被覆蓋,即使它具有相同的類,JVM也不會從它加載任何類。 – nickb