Spring rabbitmq重试邮件传递无法正常工作

时间:2016-02-23 13:08:40

标签: spring rabbitmq amqp

我正在使用RabbitMQ设置PubSub,所以每当消息处理出现故障时,在Listener中发送否定确认,理想情况下应在一些预先配置的时间间隔后重试,但是每隔一秒重复一次,直到消息时间为止 - ttl值。对于重试,在SimpleMessageListenerContainer中添加了创建的Advice链。根据订阅动态创建队列和使用者。以下是rabbitmq服务的代码

@Service

public class RabbitMQQueueServiceImpl {

    private final String JAVA_OBJECT_SERIALIZER_HEADER_NAME = "Content-Type";
    private final String JAVA_OBJECT_SERIALIZER_HEADER_VALUE = "application/x-java-serialized-object";
    private final String TIME_TO_LIVE_HEADER_NAME = "x-message-ttl";

    @Value("${rabbitmq.message.timetolive}")
    private Long messageTimeToLive;

    @Value("${rabbitmq.host}")
    private String host;

    @Value("${rabbitmq.username}")
    private String userName;

    @Value("${rabbitmq.password}")
    private String password;

    @Value("${rabbitmq.port}")
    private Integer port;

    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init() {
        createRabitTemplate();

    }

    public void subscribe(WebHookSubscription webHookSubscription,String queueName) throws Exception {

        createProducer(webHookSubscription,queueName);
        createConsumer(webHookSubscription,queueName);

    }

    public void publish(PublishEvent publishEvent,String queueName) {

        //Append eventId for queueName and exchange name as accountId_accountTypeId

        String exchange = queueName;
        queueName = queueName+"_"+publishEvent.getEventId();


        BroadcastMessageBean broadcastMessageBean = new BroadcastMessageBean();

        /*
         * Set properties for message - message persistence, JAVA object serializer
         */
        MessageProperties messageProperties = new MessageProperties();

        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);

        messageProperties.setHeader(JAVA_OBJECT_SERIALIZER_HEADER_NAME, JAVA_OBJECT_SERIALIZER_HEADER_VALUE);
        BeanUtils.copyProperties(publishEvent, broadcastMessageBean);
        Gson gson = new Gson();

        Message messageBean = new Message(gson.toJson(broadcastMessageBean).getBytes(), messageProperties);
        rabbitTemplate.send(exchange, publishEvent.getEventId().toString(), messageBean);

    }

    public void createQueue(String queueName) {

        RabbitTemplate rabbitTemplate = getRabbitTemplate(queueName);
        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());

        Queue queue = new Queue(queueName, true,false,false);

        rabbitAdmin.declareQueue(queue);

    }

    private void createProducer(WebHookSubscription webHookSubscription, String queueName) {

        queueName = queueName+"_"+webHookSubscription.getEventId();

        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());

        String exchangeName = webHookSubscription.getAccountId()+"_"+webHookSubscription.getAccountTypeId();
        String routingKey = webHookSubscription.getEventId().toString();


        Map<String, Object> arguments = new HashMap<String, Object>();
        arguments.put(TIME_TO_LIVE_HEADER_NAME, messageTimeToLive);

        Queue queue = new Queue(queueName, true,false,false, arguments );
        rabbitAdmin.declareQueue(queue);

        Exchange exchange = new TopicExchange(exchangeName, true, false) ;
        rabbitAdmin.declareExchange(exchange );

        Binding binding =  new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, arguments);
        rabbitAdmin.declareBinding(binding );

    }

    private void createConsumer(WebHookSubscription webHookSubscription,String queueName) throws Exception {

        Map<String, String> headers = new HashMap<String, String>();

        //Create exchange name same as queue name and append eventId to queueName for each event subscription for account
        queueName = queueName+"_"+webHookSubscription.getEventId();

        for(WebHookSubscriptionHeaders header : webHookSubscription.getWebHookHeaders()) {
            headers.put(header.getName(), header.getValue());
        }


        /*
         * Register consumer through SimpleMessageListenerContainer
         * add consumer handler with eventId 
         */
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();

        container.setPrefetchCount(10);
        container.setRecoveryInterval(1000);

        ConsumerHandler consumerHandler = new ConsumerHandler(webHookSubscription.getEventId(),
                webHookSubscription.getWebHookURL(),headers, webHookSubscription.getNotificationEmail());

        container.setConnectionFactory(connectionFactory());
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        container.setAdviceChain(new Advice[]{retryAdvice()});

        /*
         * Register consumer to queue and all messages will be broadcasted to subscriber specific queue 
         */
        container.setQueues(new Queue(queueName,true,false,false));
        container.setMessageListener(consumerHandler);
        container.start();

    }


    private MethodInterceptor retryAdvice() {
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();

        backOffPolicy.setInitialInterval(15000);
        backOffPolicy.setMultiplier(100);
        backOffPolicy.setMaxInterval(604800);

        return RetryInterceptorBuilder.stateless().backOffPolicy(backOffPolicy).maxAttempts(5).recoverer(new RejectAndDontRequeueRecoverer()).build();
    }

    /**
     * Create Connection factory for RabbitMQ template
     * @return
     */
    private CachingConnectionFactory connectionFactory() {

        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(getHost());
        connectionFactory.setUsername(getUserName());
        connectionFactory.setPassword(getPassword());
        connectionFactory.setPort(getPort());

        return connectionFactory;
    }

    private RabbitTemplate createRabitTemplate() {

        rabbitTemplate = new RabbitTemplate(connectionFactory());


        rabbitTemplate.setMessageConverter(new JsonMessageConverter());

        //Create retries configuration for message delivery
        /*RetryTemplate retryTemplate = new RetryTemplate();
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(messageRetriesInitialInterval);
        backOffPolicy.setMaxInterval(messageRetriesMaxInterval);
        backOffPolicy.setMultiplier(messageRetriesMultiplier);
        retryTemplate.setBackOffPolicy(backOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(retries);
        retryTemplate.setRetryPolicy(retryPolicy);
        //Retries configuration ends here

        rabbitTemplate.setRetryTemplate(retryTemplate);*/

        return rabbitTemplate;
    }

}

这是针对消费者处理程序的

public class ConsumerHandler implements ChannelAwareMessageListener {

public ConsumerHandler(Long eventId, String url, Map<String, String> headers, String email) {

        this.eventId = eventId;
        this.url = url;
        this.headers = headers;
        this.email = email;

    }
@Override
    public void onMessage(Message message, Channel channel) throws IOException, NoSuchAlgorithmException {

  //process message body
   if(error){   
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
    return;
   }
   //for success
   channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
 }

}

上述代码中缺少什么才能重试?

日志:

2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Storing delivery for Consumer: tags=[{amq.ctag-a1IonIIN7mf8uhIGXE4eIw=14947_0_4}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest@127.0.0.1:5672/,1), acknowledgeMode=MANUAL local queue size=0>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Exiting proxied method in stateful retry with result: (null)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Exiting proxied method in stateful retry with result: (null)>
2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Retrieving delivery for Consumer: tags=[{amq.ctag-a1IonIIN7mf8uhIGXE4eIw=14947_0_4}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest@127.0.0.1:5672/,1), acknowledgeMode=MANUAL local queue size=1>
2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Received message: (Body:'[B@4109b410(byte[199])'MessageProperties [headers={eventId=4, retry=1, Content-Type=application/x-java-serialized-object}, timestamp=null, messageId=31b93494-d6ef-4d63-b90b-c7a7e73acb70, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=application/octet-stream, contentEncoding=null, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=true, receivedExchange=14947_0, receivedRoutingKey=4, deliveryTag=10234, messageCount=0])>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Executing proxied method in stateful retry: public abstract void org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$ContainerDelegate.invokeListener(com.rabbitmq.client.Channel,org.springframework.amqp.core.Message) throws java.lang.Exception(27f49de4)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.support.RetryTemplate] - <Retry: count=0>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Executing proxied method in stateful retry: public abstract void org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$ContainerDelegate.invokeListener(com.rabbitmq.client.Channel,org.springframework.amqp.core.Message) throws java.lang.Exception(27f49de4)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.support.RetryTemplate] - <Retry: count=0>

1 个答案:

答案 0 :(得分:0)

没有直接的方法来确保延迟重试。 您可能必须创建一个死信交换并在该交换上创建一个retryQueue,并将消息ttl设置为您想要的延迟间隔。

您的主要队列应该是这样的

@Bean
public Queue mainQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-dead-letter-exchange", "retryExchangeName");
    return new Queue("mainQueue", true, false, false, args);
}

并重试队列

@Bean
public Queue retryQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-dead-letter-exchange", "mainExchangeName");
    args.put("x-message-ttl", RETRY_MESSAGE_TTL);
    return new Queue("retryQueue", true, false, false, args);
}

现在对于你想要延迟重试的消息Nack with requeue = false

channel().basicNack(10, false, false);

现在此消息将被发送到retryQueue,在指定的ttl之后,它将被放回mainQueue进行重新处理。