springboot acknowledgment message producers to achieve integration rabbitmq, badmail switch, not routed to the message queue

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/fu_huo_1993/article/details/88224985

    In the last article  springboot integration rabbitmq in , we realized the simple integration springboot and rabbitmq of this article is mainly to enhance the functionality of the article, it performs the following functions.

demand:

    Producers at boot time, automatically created queue, the binding, the switch and set good death letter exchange, the backup switch (alternate-exchange). After the message is sent by the producer, the producer side of the message needs to be sent an acknowledgment of the received message RabbitMQ. To test the message and not the dead letter routing message, the sender, the transmission 11 is normal, the message may be routed to a message queue, sending a message not routed to a message queue, so that switch into the alternate-exchange in. The recipient after receiving the message, the random number rejection message, make it into the x-dead-letter-exchange medium.

Achieve the following functions:

  1, @Bean mode automatically queues, switch, create binding.
  2, using @RabbitListener message queue listener.
  3, acknowledgment message producers to achieve.
  4, to achieve badmail exchanger (expired messages, basic.nack or basic.reject and requeue parameter is false or queue full message will enter the switch).
  5, backup switch (alternate-exchange), proper routing of the message will not pass through this switch.

Important to realize part of the functions:

  1, message producers confirmed

          | - spring.rabbitmq.template.mandatory = true set to true

          |- spring.rabbitmq.publisher-confirms = true 设置成true

          | - Write a java class that implements the interface RabbitTemplate.ConfirmCallback, in which we can confirm that the message has reached the RabbitMQ server.

  2, implemented exchanger badmail

          | - declare the queue when setting x-dead-letter-exchange parameters

  3, the routing message processing is not

          | - stated when the switch setting alternate-exchange parameters

Steps are as follows:

  1, jar package introduced, producers and consumers alike

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 

 2, the producer - profile

server:
  port: 9088
spring:
  rabbitmq:
    host: 140.143.237.224
    port: 5672
    username: root
    password: root
    virtual-host: /
    connection-timeout: 10000
    template:
      mandatory: true
    publisher-confirms: true

  Note: Here you need and publisher-confirms mandatory parameter to true

  3, producer - producer written confirmation messages

@Slf4j
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback {

	@Override
	public void confirm(CorrelationData correlationData, boolean ack, String cause) {
		log.info("(start)生产者消息确认=========================");
		log.info("correlationData:[{}]", correlationData);
		log.info("ack:[{}]", ack);
		log.info("cause:[{}]", cause);
		if (!ack) {
			log.info("消息可能未到达rabbitmq服务器");
		}
		log.info("(end)生产者消息确认=========================");
	}
}

  Note: here you need to achieve RabbitTemplate.ConfirmCallback interface, message confirmation

  4, producer - producer Configuration

@Configuration
public class RabbitmqConfiguration {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@PostConstruct
	public void initRabbitTemplate() {
		// 设置生产者消息确认
		rabbitTemplate.setConfirmCallback(new RabbitConfirmCallback());
	}

	/**
	 * 申明队列
	 *
	 * @return
	 */
	@Bean
	public Queue queue() {
		Map<String, Object> arguments = new HashMap<>(4);
		// 申明死信交换器
		arguments.put("x-dead-letter-exchange", "exchange-dlx");
		return new Queue("queue-rabbit-springboot-advance", true, false, false, arguments);
	}

	/**
	 * 没有路由到的消息将进入此队列
	 *
	 * @return
	 */
	@Bean
	public Queue unRouteQueue() {
		return new Queue("queue-unroute");
	}

	/**
	 * 死信队列
	 *
	 * @return
	 */
	@Bean
	public Queue dlxQueue() {
		return new Queue("dlx-queue");
	}

	/**
	 * 申明交换器
	 *
	 * @return
	 */
	@Bean
	public Exchange exchange() {
		Map<String, Object> arguments = new HashMap<>(4);
		// 当发往exchange-rabbit-springboot-advance的消息,routingKey和bindingKey没有匹配上时,将会由exchange-unroute交换器进行处理
		arguments.put("alternate-exchange", "exchange-unroute");
		return new DirectExchange("exchange-rabbit-springboot-advance", true, false, arguments);
	}

	@Bean
	public FanoutExchange unRouteExchange() {
		// 此处的交换器的名字要和 exchange() 方法中 alternate-exchange 参数的值一致
		return new FanoutExchange("exchange-unroute");
	}

	/**
	 * 申明死信交换器
	 *
	 * @return
	 */
	@Bean
	public FanoutExchange dlxExchange() {
		return new FanoutExchange("exchange-dlx");
	}

	/**
	 * 申明绑定
	 *
	 * @return
	 */
	@Bean
	public Binding binding() {
		return BindingBuilder.bind(queue()).to(exchange()).with("product").noargs();
	}

	@Bean
	public Binding unRouteBinding() {
		return BindingBuilder.bind(unRouteQueue()).to(unRouteExchange());
	}

	@Bean
	public Binding dlxBinding() {
		return BindingBuilder.bind(dlxQueue()).to(dlxExchange());
	}
}

   Note: the value of x-dead-letter-exchange and alternate-exchange parameter values and exchangers need to be consistent

 5, producer - to write the message sender

@Component
@Slf4j
public class RabbitProducer implements ApplicationListener<ContextRefreshedEvent> {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		String exchange = "exchange-rabbit-springboot-advance";
		String routingKey = "product";
		String unRoutingKey = "norProduct";

		// 1.发送一条正常的消息 CorrelationData唯一(可以在ConfirmListener中确认消息)
		IntStream.rangeClosed(0, 10).forEach(num -> {
			String message = LocalDateTime.now().toString() + "发送第" + (num + 1) + "条消息.";
			rabbitTemplate.convertAndSend(exchange, routingKey, message, new CorrelationData("routing" + UUID.randomUUID().toString()));
			log.info("发送一条消息,exchange:[{}],routingKey:[{}],message:[{}]", exchange, routingKey, message);
		});
		// 2.发送一条未被路由的消息,此消息将会进入备份交换器(alternate exchange)
		String message = LocalDateTime.now().toString() + "发送一条消息.";
		rabbitTemplate.convertAndSend(exchange, unRoutingKey, message, new CorrelationData("unRouting-" + UUID.randomUUID().toString()));
		log.info("发送一条消息,exchange:[{}],routingKey:[{}],message:[{}]", exchange, unRoutingKey, message);
	}
}

   Note: 1, where message 2 is transmitted a message can be properly routed to the message queue, the other does not exist since routingKey, and therefore will not be routed to the queue, there is no message was observed this route bound to the queue of alternate-exchange.

               2, CorrelationData require unique data, this value can be used to confirm the message producer.

  6, producer - start class

@SpringBootApplication
public class ProducerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProducerApplication.class, args);
	}
}

 

  7. Consumers - Profile

server:
  port: 9087
spring:
  rabbitmq:
    host: 140.143.237.224
    port: 5672
    username: root
    password: root
    virtual-host: /
    connection-timeout: 10000
    listener:
      simple:
        acknowledge-mode: manual # 手动应答
        auto-startup: true
        default-requeue-rejected: false # 不重回队列
        concurrency: 5
        max-concurrency: 20
        prefetch: 1 # 每次只处理一个信息
        retry:
          enabled: true

 

  8, the consumer - message received

@Component
@Slf4j
public class RabbitConsumer {

	/**
	 * 监听 queue-rabbit-springboot-advance 队列
	 *
	 * @param receiveMessage 接收到的消息
	 * @param message
	 * @param channel
	 */
	@RabbitListener(queues = "queue-rabbit-springboot-advance")
	public void receiveMessage(String receiveMessage, Message message, Channel channel) {
		try {
			// 手动签收
			log.info("接收到消息:[{}]", receiveMessage);
			if (new Random().nextInt(10) < 5) {
				log.warn("拒绝一条信息:[{}],此消息将会由死信交换器进行路由.", receiveMessage);
				channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
			} else {
				channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			}
		} catch (Exception e) {
			log.info("接收到消息之后的处理发生异常.", e);
			try {
				channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			} catch (IOException e1) {
				log.error("签收异常.", e1);
			}
		}
	}
}

  Note: The consumer receives several messages will reject random, this observation has not entered the message queue x-dead-letter-exchange the bound exchanger.

  9, the results
 

Complete code:

Code is as follows: https://gitee.com/huan1993/rabbitmq/tree/master/rabbitmq-springboot-advanced

Guess you like

Origin blog.csdn.net/fu_huo_1993/article/details/88224985