Spring AMQP and ShutdownSignalException

倖福魔咒の 提交于 2019-12-25 08:25:10

问题


I have an AMQP Project with Spring AMQP. The RabbitMQ Server is not mine, so I have no control over it. When my application starts, it makes a private response queue like this:

@Bean(name="myAnonymousResponseQueue")
public Queue myAnonymousResponseQueue() 
{       
    Queue q = myAmqpAdmin().declareQueue();
    return q;
}

And I have a SimpleMessageListenerContariner like this:

    @Bean 
    public SimpleMessageListenerContainer myResponseMessageListenerContainer() 
    {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(myConnectionFactory());
        container.setQueues(myAnonymousResponseQueue());
        container.setMessageListener(myRabbitTemplate());
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        container.setMessageConverter(myMessageConverter());
        container.setErrorHandler(myResponseErrorHandler());
        container.setAutoStartup(true);
        container.setRabbitAdmin(myAmqpAdmin());
        return container;
    }

I've had connection problems lately (ShutdownSignalException). And the problem is that I can not regenerate the private queue. First, this is the connection error:

com.rabbitmq.client.ShutdownSignalException: connection error
    at com.rabbitmq.client.impl.AMQConnection.startShutdown(AMQConnection.java:739)
    at com.rabbitmq.client.impl.AMQConnection.shutdown(AMQConnection.java:729)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:573)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.read(Unknown Source)
    at sun.security.ssl.InputRecord.readFully(Unknown Source)
    at sun.security.ssl.InputRecord.read(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
    at sun.security.ssl.AppInputStream.read(Unknown Source)
    at java.io.BufferedInputStream.fill(Unknown Source)
    at java.io.BufferedInputStream.read(Unknown Source)
    at java.io.DataInputStream.readUnsignedByte(Unknown Source)
    at com.rabbitmq.client.impl.Frame.readFrom(Frame.java:95)
    at com.rabbitmq.client.impl.SocketFrameHandler.readFrame(SocketFrameHandler.java:139)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:538)
    ... 1 more

But when the connection has been restored, the private queue can not be recreated:

AbstractConnectionFactory.java|291||Created new connection: SimpleConnection@289cc201 [delegate=amqp://USER@XX.XX.XX.XX:50310/sob]

RabbitAdmin.java|442||Auto-declaring a non-durable, auto-delete, or exclusive Queue (amq.gen-SLigrYFVMvllGTS5m_3AzQ) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.

com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.gen-SLigrYFVMvllGTS5m_3AzQ' in vhost 'sob', class-id=50, method-id=10)

BlockingQueueConsumer.java|565||Failed to declare queue:amq.gen-SLigrYFVMvllGTS5m_3AzQ

BlockingQueueConsumer.java|479||Queue declaration failed; retries left=3
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[amq.gen-SLigrYFVMvllGTS5m_3AzQ]
    at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:571)
    at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:470)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1171)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:106)
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:102)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:124)
    at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:885)
    at com.rabbitmq.client.impl.ChannelN.queueDeclarePassive(ChannelN.java:61)
    at org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl.queueDeclarePassive(PublisherCallbackChannelImpl.java:383)
    at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:835)
    at com.sun.proxy.$Proxy94.queueDeclarePassive(Unknown Source)
    at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:550)
    ... 3 more
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.gen-SLigrYFVMvllGTS5m_3AzQ' in vhost 'sob', class-id=50, method-id=10)
    at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:67)
    at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:33)
    at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:361)
    at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:226)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:118)
    ... 12 more
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.gen-SLigrYFVMvllGTS5m_3AzQ' in vhost 'sob', class-id=50, method-id=10)
    at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:484)
    at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:321)
    at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:144)
    at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:91)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:556)
    ... 1 more

And at the end I have this:

com.rabbitmq.client.ShutdownSignalException: clean channel shutdown; protocol method: #method<channel.close>(reply-code=200, reply-text=OK, class-id=0, method-id=0)
    at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:554)
    at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:509)
    at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:503)
    at org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl.close(PublisherCallbackChannelImpl.java:642)
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler$1.run(CachingConnectionFactory.java:946)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

So the AMQP connection is OK, but the private queue isn’t.

I do not understand why this happens. How can I obtain more information? Is it possible to recover from this error?

Thanks

---------------- (28/11/2016) -----------

The reason I have the anonymous queue defined this way, is that with:

@Bean(name="myAnonymousResponseQueue")
    public Queue myAnonymousResponseQueue() 
    {       
        return new AnonymousQueue(new Base64UrlNamingStrategy("amq.gen-")); 
    }

The server does not allow me to create it and I get the following error:

|28-11-2016 08:43:13.203|INFO |org.springframework.amqp.rabbit.core.RabbitAdmin|initialize|RabbitAdmin.java|493||Auto-declaring a non-durable, auto-delete, or exclusive Queue (amq.gen-JUYnhLX2SpqNoJT6ioB8GA) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.

com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.gen-JUYnhLX2SpqNoJT6ioB8GA' in vhost 'sob', class-id=50, method-id=10)

|28-11-2016 08:43:13.486|WARN |org.springframework.amqp.rabbit.listener.BlockingQueueConsumer|attemptPassiveDeclarations|BlockingQueueConsumer.java|581||Failed to declare queue:amq.gen-JUYnhLX2SpqNoJT6ioB8GA
|28-11-2016 08:43:13.486|WARN |org.springframework.amqp.rabbit.listener.BlockingQueueConsumer|start|BlockingQueueConsumer.java|495||Queue declaration failed; retries left=3
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[amq.gen-JUYnhLX2SpqNoJT6ioB8GA]

|28-11-2016 08:43:28.868|WARN |org.springframework.amqp.rabbit.listener.BlockingQueueConsumer|attemptPassiveDeclarations|BlockingQueueConsumer.java|581||Failed to declare queue:amq.gen-JUYnhLX2SpqNoJT6ioB8GA
|28-11-2016 08:43:28.869|ERROR|org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer|run|SimpleMessageListenerContainer.java|1372||Consumer received fatal exception on startup
org.springframework.amqp.rabbit.listener.QueuesNotAvailableException: Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it.

So, what is de difference between AmqpAdmin().declareQueue() and AnonymousQueue? Is it posible that the broker does not permit name the queue?


Now I think I understand the problema. I think my user can only create queues named “amq.gen-”. If I try with any other name I get:

com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=403, reply-text=ACCESS_REFUSED - access to queue '4788a39b-fffe-4eae-b252-8d842234a018' in vhost 'sob' refused for user 'USER', class-id=50, method-id=10)

com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=403, reply-text=ACCESS_REFUSED - access to queue 'amq-_zoOEt5jTcqMduGWNyJ4Zg' in vhost 'sob' refused for user 'USER', class-id=50, method-id=10)

So, If I can use only broker generates queues, and I need to redeclare it in a reconection, what can i do?

Thank you again.

EDIT

I'm trying to apply the work-around. I have declared a ConnectionListener with:

@Override
public void onCreate(Connection arg0) 
{
    myResponseMessageListenerContainer.stop();
    String[] colaAnterior = myResponseMessageListenerContainer.getQueueNames();

    Queue q = myAmqpAdmin.declareQueue();
    q.setAdminsThatShouldDeclare(myAmqpAdmin);
    q.setShouldDeclare(true);

    myResponseMessageListenerContainer.addQueueNames(q.getName());

    myResponseMessageListenerContainer.removeQueueNames(colaAnterior);
    myResponseMessageListenerContainer.initialize();
    myResponseMessageListenerContainer.start(); 
}

But now I have this error:

|30-11-2016 10:56:17.312|ERROR|org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer|redeclareElementsIfNecessary|SimpleMessageListenerContainer.java|1116||Failed to check/redeclare auto-delete queue(s).
    org.springframework.amqp.UncategorizedAmqpException: java.lang.IllegalStateException: Listener expects us to be listening on '[amq.gen-Xg5MG5n42ecpoW4-DA198A]'; our queues: [amq.gen-2qRLgfUmMxskshFCi1dzuA]
        at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:80)
        at org.springframework.amqp.rabbit.connection.RabbitAccessor.convertRabbitAccessException(RabbitAccessor.java:113)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.start(AbstractMessageListenerContainer.java:553)
        at es.omie.amqp.config.listener.XBIDConnectionListener.onCreate(XBIDConnectionListener.java:57)
        at org.springframework.amqp.rabbit.connection.CompositeConnectionListener.onCreate(CompositeConnectionListener.java:33)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:553)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createBareChannel(CachingConnectionFactory.java:500)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.getCachedChannelProxy(CachingConnectionFactory.java:474)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.getChannel(CachingConnectionFactory.java:467)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.access$1500(CachingConnectionFactory.java:97)
        at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$ChannelCachingConnectionProxy.createChannel(CachingConnectionFactory.java:1084)
        at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:1394)
        at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:1370)
        at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:1346)
        at org.springframework.amqp.rabbit.core.RabbitAdmin.getQueueProperties(RabbitAdmin.java:335)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.redeclareElementsIfNecessary(SimpleMessageListenerContainer.java:1102)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:95)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1278)
        at java.lang.Thread.run(Unknown Source)
    Caused by: java.lang.IllegalStateException: Listener expects us to be listening on '[amq.gen-Xg5MG5n42ecpoW4-DA198A]'; our queues: [amq.gen-2qRLgfUmMxskshFCi1dzuA]
        at org.springframework.util.Assert.state(Assert.java:392)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doStart(SimpleMessageListenerContainer.java:770)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.start(AbstractMessageListenerContainer.java:550)
        ... 16 more

I keep trying, but any ideas on how to fix it?

Thanks

EDIT 2

Now I have this:

@Override
    public void onCreate(Connection arg0) 
    {
        myResponseMessageListenerContainer.stop();

        String[] colaAnterior = myResponseMessageListenerContainer.getQueueNames();
        Queue q = myAmqpAdmin.declareQueue();

        log.info(" ------ RESPONSE QUEUE -> OLD NAME: " + Arrays.asList(colaAnterior) + " NEW NAME: " + q.getName());

        myResponseMessageListenerContainer.addQueueNames(q.getName());
        myRabbitTemplate.setReplyAddress(q.getName());
        myRabbitTemplate.setQueue(q.getName());

        myResponseMessageListenerContainer.removeQueueNames(colaAnterior);

        myResponseMessageListenerContainer.shutdown();
        myResponseMessageListenerContainer.initialize();
        myResponseMessageListenerContainer.start(); 
    }

But the container does not stay with the last defined queue:

|30-11-2016 16:32:19.996|INFO |es.omie.amqp.config.listener.XBIDConnectionListener|onCreate|XBIDConnectionListener.java|42|| ------ RESPONSE QUEUE -> OLD NAME: [amq.gen-uTp6TCP66x2AlXUmQzqz8g] NEW NAME: amq.gen-DOwEn8WKz_9ymCBQFiMDNg


com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'amq.gen-uTp6TCP66x2AlXUmQzqz8g' in vhost 'sob', class-id=50, method-id=10)

I can't see the new queue name.

I can see in the logs the restart of the container before the connection is created:

Restarting Consumer: tags=[{amq.ctag-KYCURTkS4EevXHQtrpYV9Q=amq.gen-uTp6TCP66x2AlXUmQzqz8g}]

But not after the start method.


回答1:


Spring AMQP provides its own AnonymousQueue for this reason; its random name will be retained so, when the connection is re-established, the declaration will be re-created. Your code lets the broker name the queue.

However, you should let the RabbitAdmin take care of the declaration automatically rather than doing it yourself.

@Bean
public Queue myAnonymousResponseQueue() {
    return new AnonymousQueue();
}

When the admin (which must also be a @Bean) detects the initial connection (or reconnect), it will declare all such queues.

See Configuring the Broker.

EDIT

If your administrator does not allow you to name the queue (you can't use amq.gen- as a prefix) you will have to re-declare the broker-generated queue and update the container with the new queue.

Add a ConnectionListener to the connection factory; when onCreate() is called (because a new connection is established), stop the container, re-declare the queue, update the container's queue to the new name; start the container.

Bear in mind that any messages in the temporary queue will be lost when the connection is dropped.

Since the listener might be called on a container thread, you should hand off this work to another thread; otherwise there will be a delay.

EDIT2

@SpringBootApplication
@EnableRabbit
public class So40802855Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So40802855Application.class, args);
        Thread.sleep(2000); // wait for reply queue setup
        RabbitTemplate template = context.getBean(RabbitTemplate.class);
        System.out.println(template.convertSendAndReceive("test.x", "foo"));
        context.getBean(CachingConnectionFactory.class).resetConnection();
        Thread.sleep(2000); // wait for reply queue setup
        System.out.println(template.convertSendAndReceive("test.x", "bar"));
        context.getBean(RabbitAdmin.class).deleteQueue("test.x");
        context.close();
        System.exit(0);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setReplyTimeout(30000);
        return rabbitTemplate;
    }

    @Bean
    public SimpleMessageListenerContainer replyContainer(ConnectionFactory connectionFactory) {
        connectionFactory.addConnectionListener(listener());
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setMessageListener(rabbitTemplate(connectionFactory));
        container.setAutoStartup(false);
        container.setDeclarationRetries(0);
        container.setFailedDeclarationRetryInterval(500);
        return container;
    }

    @Bean
    public Queue testX() {
        return new Queue("test.x");
    }

    @Bean
    public ConnectionListener listener() {
        return new MyConnectionListener();
    }

    @RabbitListener(queues = "test.x")
    public String listen(String in) {
        return in.toUpperCase();
    }

    public static class MyConnectionListener implements ConnectionListener {

        private static final Log logger = LogFactory.getLog(MyConnectionListener.class);

        @Autowired
        private RabbitTemplate template;

        @Autowired
        private AmqpAdmin admin;

        @Autowired
        private ApplicationContext applicationContext;

        @Override
        public void onCreate(Connection connection) {
            SimpleMessageListenerContainer replyContainer = applicationContext.getBean("replyContainer",
                    SimpleMessageListenerContainer.class);
            // need to stop/start asynchronously to avoid deadlock
            Executors.newSingleThreadExecutor().execute(() -> {
                if (replyContainer.isRunning()) {
                    logger.info("Waiting for the container to stop itself because of missing queue");
                    while (replyContainer.isRunning()) {
                        try {
                            Thread.sleep(100);
                        }
                        catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                    logger.info("Container stopped itself because of missing queue");
                }
                Queue queue = this.admin.declareQueue();
                logger.info("Changing queue from " + Arrays.asList(replyContainer.getQueueNames()) + " to "
                        + queue.getName());
                this.template.setReplyAddress(queue.getName());
                replyContainer.setQueues(queue);
                logger.info("Starting container");
                replyContainer.start();
            });
        }

        @Override
        public void onClose(Connection connection) {
        }

    }

 }


来源:https://stackoverflow.com/questions/40802855/spring-amqp-and-shutdownsignalexception

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!