我正在使用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>
答案 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进行重新处理。