问题
We have a spring application which is having a dynamic queue listener connecting to queue in rabbitmq. Let's say I have a total of 5 listener consumer connected to 5 queues from my spring application to rabbitmq.
Now if Network fluctuation/failure happens then each time, first one of my 5 connected queues is stopped retrying to rabbitmq.
I have debugged code through spring-amqp class and found out that on creating a connection with rabbitmq (when network failure happens), it fails to connect to it and throw org.springframework.amqp.AmqpIOException particular exception which is not handled in retry function so that that queue is removed from retried queue list.
My Main class:
@Slf4j
@SpringBootApplication(exclude = {ClientAutoConfiguration.class})
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.x.x.repositories")
@EntityScan(basePackages = "com.x.x.entities")
public class Main
{
@PostConstruct
void configuration()
{
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args)
{
ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
RabbitMQListenerUtil queueRegisterUtil = context.getBean(RabbitMQListenerUtil.class);
try
{
queueRegisterUtil.registerSpecifiedListenerForAllInstance();
}
catch (Exception e)
{
log.error(e.getMessage(), e);
}
}
}
Class which is used to create 5 consumer/listener
/**
* The Class RabbitMQListenerUtil.
*/
@Component
@Slf4j
public class RabbitMQListenerUtil
{
@Autowired
private ApplicationContext applicationContext;
public void registerSpecifiedListenerForAllInstance()
{
try
{
log.debug("New Listener has been register for instane name : ");
Thread.sleep(5000);
registerNewListener("temp1");
registerNewListener("temp2");
registerNewListener("temp3");
registerNewListener("temp4");
registerNewListener("temp5");
}
catch (Exception e)
{
}
}
/**
* This method will add new listener bean for given queue name at runtime
*
* @param queueName - Queue name
* @return Configurable application context
*/
public void registerNewListener(String queueName)
{
AnnotationConfigApplicationContext childAnnotaionConfigContext = new AnnotationConfigApplicationContext();
childAnnotaionConfigContext.setParent(applicationContext);
ConfigurableEnvironment environmentConfig = childAnnotaionConfigContext.getEnvironment();
Properties listenerProperties = new Properties();
listenerProperties.setProperty("queue.name", queueName + "_queue");
PropertiesPropertySource pps = new PropertiesPropertySource("props", listenerProperties);
environmentConfig.getPropertySources().addLast(pps);
childAnnotaionConfigContext.register(RabbitMQListenerConfig.class);
childAnnotaionConfigContext.refresh();
}
}
Class which create dynamic listener for queue consumer
/**
* The Class RabbitMQListenerConfig.
*/
@Configuration
@Slf4j
@EnableRabbit
public class RabbitMQListenerConfig
{
/** The Constant ALLOW_MESSAGE_REQUEUE. */
private static final boolean ALLOW_MESSAGE_REQUEUE = true;
/** The Constant MULTIPLE_MESSAGE_FALSE. */
private static final boolean MULTIPLE_MESSAGE_FALSE = false;
/**
* Listen.
*
* @param msg the msg
* @param channel the channel
* @param queue the queue
* @param deliveryTag the delivery tag
* @throws IOException Signals that an I/O exception has occurred.
*/
@RabbitListener(queues = "${queue.name}")
public void listen(Message msg, Channel channel, @Header(AmqpHeaders.CONSUMER_QUEUE) String queue, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException
{
int msgExecutionStatus = 0;
try
{
String message = new String(msg.getBody(), StandardCharsets.UTF_8);
log.info(message);
}
catch (Exception e)
{
log.error(e.toString());
log.error(e.getMessage(), e);
}
finally
{
ackMessage(channel, deliveryTag, msgExecutionStatus);
}
}
/**
* Ack message.
*
* @param channel the channel
* @param deliveryTag the delivery tag
* @param msgExecutionStatus the msg execution status
* @throws IOException Signals that an I/O exception has occurred.
*/
protected void ackMessage(Channel channel, long deliveryTag, int msgExecutionStatus) throws IOException
{
if (msgExecutionStatus == Constants.MESSAGE_DELETE_FOUND_EXCEPTION)
{
channel.basicNack(deliveryTag, MULTIPLE_MESSAGE_FALSE, ALLOW_MESSAGE_REQUEUE);
}
else
{
channel.basicAck(deliveryTag, MULTIPLE_MESSAGE_FALSE);
}
}
/**
* Bean will create from this with given name.
*
* @param name - Queue name-
* @return the queue
*/
@Bean
public Queue queue(@Value("${queue.name}") String name)
{
return new Queue(name);
}
/**
* RabbitAdmin Instance will be created which is required to create new Queue.
*
* @param cf - Connection factory
* @return the rabbit admin
*/
@Bean
public RabbitAdmin admin(ConnectionFactory cf)
{
return new RabbitAdmin(cf);
}
}
Application log :
https://pastebin.com/NQWdmdTH
I have tested this multiple times and each time my first connected queue is being stopped from connecting .
========================= UPDATE 1=============================
Code to reconnect stopped consumer : https://pastebin.com/VnUrhdLP
回答1:
Caused by: java.net.UnknownHostException: rabbitmqaind1.hqdev.india
There is something wrong with your network.
来源:https://stackoverflow.com/questions/55879429/spring-amqp-consumer-not-re-connecting-to-queue-after-network-failure