RabbitMQ 整合 Spring

博文目录


应用结构

在这里插入图片描述
应用分基本和高级两部分, 从配置文件到消费者到生产者都有做区分, advance:高级, base:基础

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.mrathena.middle.ware</groupId>
    <artifactId>rabbit.mq.spring</artifactId>
    <version>1.0.0</version>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.3.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

rabbitmq.properties

rabbitmq.host=116.62.162.48
rabbitmq.port=5672
rabbitmq.username=mrathena
rabbitmq.password=password
rabbitmq.virtual-host=spring

基础

生产者配置 spring-rabbitmq-producer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <!--定义管理交换机、队列-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机. 默认交换机类型为direct,名字为:"",路由键为队列的名称-->
    <!--
        id:bean的名称
        name:queue的名称
        auto-declare:自动创建
        auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列
        durable:是否持久化
    -->

    <!-- 简单模式 -->
    <!-- ignore-declaration-exceptions: 忽略队列已存在且声明时属性不一致的报错 -->
    <rabbit:queue id="queue-hello-world" name="queue-hello-world" auto-declare="true" durable="false" ignore-declaration-exceptions="true"/>

    <!--工作队列模式-->
    <rabbit:queue id="queue-work-queue" name="queue-work-queue" auto-declare="true" durable="false"/>

    <!-- 发布订阅模式 -->
    <!-- fanout: 广播, 所有绑定到交换机的队列都能收到消息 -->
    <rabbit:queue id="queue-fanout-1" name="queue-fanout-1" auto-declare="true" durable="false"/>
    <rabbit:queue id="queue-fanout-2" name="queue-fanout-2" auto-declare="true" durable="false"/>
    <rabbit:fanout-exchange id="exchange-fanout" name="exchange-fanout" auto-declare="true" durable="false">
        <rabbit:bindings>
            <rabbit:binding queue="queue-fanout-1"/>
            <rabbit:binding queue="queue-fanout-2"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <!-- 路由模式 -->
    <!-- direct: 定向, 绑定到交换机且指定的队列才能收到消息 -->
    <rabbit:queue id="queue-direct" name="queue-direct" auto-declare="true" durable="false"/>
    <rabbit:direct-exchange id="exchange-direct" name="exchange-direct">
        <rabbit:bindings>
            <!--key: 路由键-->
            <rabbit:binding queue="queue-direct" key="direct"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- 主题模式 -->
    <!-- topic: 主题, 绑定到交换机且满足条件的队列才能收到消息, 路由键:*匹配一个单词,#匹配多个单词 -->
    <rabbit:queue id="queue-topic-1" name="queue-topic-1" auto-declare="true" durable="false"/>
    <rabbit:queue id="queue-topic-2" name="queue-topic-2" auto-declare="true" durable="false"/>
    <rabbit:queue id="queue-topic-3" name="queue-topic-3" auto-declare="true" durable="false"/>
    <rabbit:topic-exchange id="exchange-topic" name="exchange-topic" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="queue-topic-1" pattern="topic.*"/>
            <rabbit:binding queue="queue-topic-2" pattern="topic.#"/>
            <rabbit:binding queue="queue-topic-3" pattern="topic.three.*"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!-- rabbitTemplate-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>

生产者测试 ProducerTest

package com.mrathena;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerBaseTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testHelloWorld() {
		rabbitTemplate.convertAndSend("queue-hello-world", "hello world");
	}

	@Test
	public void testWorkQueues() {
		rabbitTemplate.convertAndSend("queue-work-queue", "work queue");
	}

	@Test
	public void testFanout() {
		rabbitTemplate.convertAndSend("exchange-fanout", "", "fanout");
	}

	@Test
	public void testDirect() {
		rabbitTemplate.convertAndSend("exchange-direct", "direct", "direct");
	}

	@Test
	public void testTopics() {
		rabbitTemplate.convertAndSend("exchange-topic", "topic.a", "topic topic.a");
		rabbitTemplate.convertAndSend("exchange-topic", "topic.a.b", "topic topic.a.b");
		rabbitTemplate.convertAndSend("exchange-topic", "topic.three.a.b", "topic topic.three.a.b");
	}
}

消费者配置 spring-rabbitmq-consumer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <!--基础部分-->
    <bean id="queueHelloWorldListener" class="com.mrathena.rabbit.mq.listener.base.QueueHelloWorldListener"/>
    <bean id="queueWorkQueueOneListener" class="com.mrathena.rabbit.mq.listener.base.QueueWorkQueueOneListener"/>
    <bean id="queueWorkQueueTwoListener" class="com.mrathena.rabbit.mq.listener.base.QueueWorkQueueTwoListener"/>
    <bean id="queueFanoutListener" class="com.mrathena.rabbit.mq.listener.base.QueueFanoutListener"/>
    <bean id="queueDirectListener" class="com.mrathena.rabbit.mq.listener.base.QueueDirectListener"/>
    <bean id="queueTopicOneListener" class="com.mrathena.rabbit.mq.listener.base.QueueTopicOneListener"/>
    <bean id="queueTopicTwoListener" class="com.mrathena.rabbit.mq.listener.base.QueueTopicTwoListener"/>
    <bean id="queueTopicThreeListener" class="com.mrathena.rabbit.mq.listener.base.QueueTopicThreeListener"/>
    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="queueHelloWorldListener" queue-names="queue-hello-world"/>
        <rabbit:listener ref="queueWorkQueueOneListener" queue-names="queue-work-queue"/>
        <rabbit:listener ref="queueWorkQueueTwoListener" queue-names="queue-work-queue"/>
        <rabbit:listener ref="queueFanoutListener" queue-names="queue-fanout-1,queue-fanout-2"/>
        <rabbit:listener ref="queueDirectListener" queue-names="queue-direct"/>
        <rabbit:listener ref="queueTopicOneListener" queue-names="queue-topic-1"/>
        <rabbit:listener ref="queueTopicTwoListener" queue-names="queue-topic-2"/>
        <rabbit:listener ref="queueTopicThreeListener" queue-names="queue-topic-3"/>
    </rabbit:listener-container>
</beans>

消费者测试 QueueHelloWorldListener

package com.mrathena.rabbit.mq.listener.base;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

@Slf4j
public class QueueHelloWorldListener implements MessageListener {
	@Override
	public void onMessage(Message message) {
		log.info("{}", new String(message.getBody()));
	}
}

还有一些Listener都是一样的内容, 只是类名不同, 通过日志打印的类名查看区别
com.mrathena.rabbit.mq.listener.base.QueueWorkQueueOneListener
com.mrathena.rabbit.mq.listener.base.QueueWorkQueueTwoListener
com.mrathena.rabbit.mq.listener.base.QueueDirectListener
com.mrathena.rabbit.mq.listener.base.QueueFanoutListener
com.mrathena.rabbit.mq.listener.base.QueueTopicOneListener
com.mrathena.rabbit.mq.listener.base.QueueTopicTwoListener
com.mrathena.rabbit.mq.listener.base.QueueTopicThreeListener

高级特性

生产者 confirm return

生产者配置

<!--publisher-confirms="true"在新版本中被替换成了confirm-type="CORRELATED"-->
<!--spring.rabbitmq.publisher-confirms在新版本中被替换成了spring.rabbitmq.publisher-confirm-type=correlated-->
<rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"
                               confirm-type="CORRELATED"
                               publisher-returns="true"/>

<!-- 生产者 confirm return -->
<rabbit:queue id="queue-confirm-return" name="queue-confirm-return" auto-declare="true" durable="false"/>
<rabbit:direct-exchange id="exchange-confirm-return" name="exchange-confirm-return">
    <rabbit:bindings>
        <rabbit:binding queue="queue-confirm-return" key="confirm"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

生产者测试

package com.mrathena.advance;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class ProducerConfirmReturnTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testConfirm() {
		rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
			@Override
			public void confirm(CorrelationData correlationData, boolean ack, String cause) {
				if (ack) {
					System.out.println("broker 接收消息");
				} else {
					System.out.println("broker 拒收消息: " + cause);
				}
			}
		});
		rabbitTemplate.convertAndSend("exchange-confirm-return", "confirm", "direct");
		// 消息从生产者到不了交换机的会被拒收
		rabbitTemplate.convertAndSend("exchange-confirm-return-not-exist", "confirm", "direct");
		rabbitTemplate.convertAndSend("exchange-confirm-return", "confirm.not.exist", "direct");
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Test
	public void testReturn() {
		rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
			@Override
			public void returnedMessage(ReturnedMessage returned) {
				System.out.println("消息被退回: " + returned);
			}
		});
		// 消息从交换机到不了队列的会被退回(这里演示不设置rabbitTemplate.setMandatory(true);的情况)
		rabbitTemplate.convertAndSend("exchange-confirm-return", "confirm.not.exist", "direct");
		// 设置交换机处理失败消息的模式 为true的时候,消息达到不了 队列时,会将消息重新返回给生产者
		rabbitTemplate.setMandatory(true);
		// 消息从交换机到不了队列的会被退回(这里演示设置了rabbitTemplate.setMandatory(true);的情况)
		rabbitTemplate.convertAndSend("exchange-confirm-return", "confirm.not.exist", "direct");
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

消费者 ack

消费者配置

<!-- 消费端ack -->
<bean id="ackListener" class="com.mrathena.rabbit.mq.listener.advance.AckListener"/>
<!-- acknowledge="manual":手动签收 -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
    <rabbit:listener ref="ackListener" queue-names="queue-ack"/>
</rabbit:listener-container>

消费者监听器

package com.mrathena.rabbit.mq.listener.advance;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

import java.util.concurrent.TimeUnit;

@Slf4j
public class AckListener implements ChannelAwareMessageListener {
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		long deliveryTag = message.getMessageProperties().getDeliveryTag();
		try {
			log.info("{}", new String(message.getBody()));
			// 通过修改, 模拟出现异常
//			int i = 5 / 0;
			int i = 5 / 1;
			channel.basicAck(deliveryTag, true);
		} catch (Exception e) {
			// 拒绝签收
			// 第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
			// 如果是false, 则消息被丢弃
			channel.basicNack(deliveryTag, true, true);
		}
	}
}

生产者配置

<!-- 消费者ack -->
<rabbit:queue id="queue-ack" name="queue-ack" auto-declare="true" durable="false"/>
<rabbit:direct-exchange id="exchange-ack" name="exchange-ack">
    <rabbit:bindings>
        <rabbit:binding queue="queue-ack" key="ack"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

生产者测试

package com.mrathena.advance;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class CustomerAckTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testAck() {
		rabbitTemplate.convertAndSend("exchange-ack", "ack", "direct");
	}
}

消费端限流

消费者配置

<!-- 消费端限流 -->
<bean id="qosListener" class="com.mrathena.rabbit.mq.listener.advance.QosListener"/>
<!--定义监听器容器 acknowledge="manual":手动签收 prefetch="1":每次抓取多少条消息 -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="1">
    <rabbit:listener ref="qosListener" queue-names="queue-qos"/>
</rabbit:listener-container>

消费者监听器

package com.mrathena.rabbit.mq.listener.advance;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

import java.util.concurrent.TimeUnit;

@Slf4j
public class QosListener implements ChannelAwareMessageListener  {
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		log.info("{}", new String(message.getBody()));
		try {
			// 便于观察一次拿几条的效果
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
	}
}

生产者配置

<!-- 消费端限流 -->
<rabbit:queue id="queue-qos" name="queue-qos" auto-declare="true" durable="false"/>
<rabbit:direct-exchange id="exchange-qos" name="exchange-qos">
    <rabbit:bindings>
        <rabbit:binding queue="queue-qos" key="qos"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

生产者测试

package com.mrathena.advance;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class CustomerQosTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testQos() {
		for (int i = 0; i < 10; i++) {
			rabbitTemplate.convertAndSend("exchange-qos", "qos", "direct-" + i);
		}
	}
}

队列和消息 ttl

消费者配置

<!-- 队列和消息ttl -->
<bean id="ttlListener" class="com.mrathena.rabbit.mq.listener.advance.TtlListener"/>
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
    <rabbit:listener ref="ttlListener" queue-names="queue-ttl"/>
</rabbit:listener-container>

消费者监听器

package com.mrathena.rabbit.mq.listener.advance;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

import java.util.concurrent.TimeUnit;

@Slf4j
public class TtlListener implements ChannelAwareMessageListener  {
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		log.info("{}", new String(message.getBody()));
		channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
	}
}

生产者配置

<!-- 消费端限流 -->
<!-- 消息和队列ttl -->
<rabbit:queue id="queue-ttl" name="queue-ttl" auto-declare="true" durable="false">
    <rabbit:queue-arguments>
        <!-- x-message-ttl指队列的过期时间, 单位:ms(毫秒), 这里设定队列过期时间是10秒 -->
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
    </rabbit:queue-arguments>
</rabbit:queue>

生产者测试

package com.mrathena.advance;

import com.mrathena.rabbit.mq.listener.advance.Application;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class QueueAndMessageTtlTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testTtl() {
		// 消息后置处理器
		// 设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期
		// 如果两者都进行了设置,以时间短的为准
		// 但是我在Web管理页面直接取消息, 发现即使消息的ttl已经过期, 但是仍然在队列里面, 队列过期后才会消失
		// 通过启动消费端TtlListener也是一样的结果, 这就很奇怪了
		MessagePostProcessor fiveSecondsMessagePostProcessor = new MessagePostProcessor() {
			@Override
			public Message postProcessMessage(Message message) throws AmqpException {
				message.getMessageProperties().getHeaders().put("expiration", 1000);
				return message;
			}
		};
		MessagePostProcessor fifteenSecondsMessagePostProcessor = new MessagePostProcessor() {
			@Override
			public Message postProcessMessage(Message message) throws AmqpException {
				message.getMessageProperties().getHeaders().put("expiration", 20 * 1000);
				return message;
			}
		};
		Object message = "message 1";
		rabbitTemplate.convertAndSend("queue-ttl", message, fiveSecondsMessagePostProcessor);
		message = "message 2";
		rabbitTemplate.convertAndSend("queue-ttl", message, fifteenSecondsMessagePostProcessor);

		// 配合启动ttl监听器, 看看能拿到几个消息
	}
}

死信队列

消费者配置

<!-- 死信队列 -->
<bean id="deadLetterListener" class="com.mrathena.rabbit.mq.listener.advance.DeadLetterListener"/>
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
    <rabbit:listener ref="deadLetterListener" queue-names="queue-dead-letter"/>
</rabbit:listener-container>

消费者监听器

package com.mrathena.rabbit.mq.listener.advance;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

@Slf4j
public class DeadLetterListener implements ChannelAwareMessageListener  {
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		log.info("{}", new String(message.getBody()));
		channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
	}
}

生产者配置

<!-- 死信队列 -->
<!-- 其实就是一个普通队列, 只不过是用来接收死信而已 -->
<!-- 死信: 1. 超过队列最大长度的消息, 放不进去了, 就会变成死信. 这里只测试这一种 -->
<!-- 死信: 2. 队列超时还没有被消费的消息, 就会变成死信, 这种其实就是延时队列, 在延时队列里测试 -->
<!-- 死信: 3. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false, 懒得测了 -->
<!-- 测试方式: 给queue-dead-letter添加消费者, 然后一直给queue-max-length-3发送消息, 第四条起, 消费者将受到消息 -->
<rabbit:queue id="queue-max-length-3" name="queue-max-length-3" auto-declare="true" durable="false">
    <rabbit:queue-arguments>
        <entry key="x-max-length" value="3" value-type="java.lang.Integer"/>
        <!-- 该队列中, 如果新消息无法放入, 则把消息扔到exchange-dead-letter交换机, 使用的routingKey是dead.letter -->
        <entry key="x-dead-letter-exchange" value="exchange-dead-letter"/>
        <entry key="x-dead-letter-routing-key" value="dead.letter"/>
    </rabbit:queue-arguments>
</rabbit:queue>
<rabbit:queue id="queue-dead-letter" name="queue-dead-letter" auto-declare="true" durable="false"/>
<rabbit:direct-exchange id="exchange-dead-letter" name="exchange-dead-letter">
    <rabbit:bindings>
        <rabbit:binding queue="queue-dead-letter" key="dead.letter"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

生产者测试

package com.mrathena.advance;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class DeadLetterTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testDeadLetter() {
		rabbitTemplate.convertAndSend("", "queue-max-length-3", "dead.letter");
	}
}

延时队列

消费者配置

<!-- 延时队列 -->
<bean id="delayListener" class="com.mrathena.rabbit.mq.listener.advance.DelayListener"/>
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
    <rabbit:listener ref="delayListener" queue-names="queue-delay"/>
</rabbit:listener-container>

消费者监听器

package com.mrathena.rabbit.mq.listener.advance;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;

@Slf4j
public class DelayListener implements ChannelAwareMessageListener  {
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		log.info("{}", new String(message.getBody()));
		channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
	}
}

生产者配置

<!-- 延时队列 -->
<!-- 本质就是ttl队列加死信队列, ttl队列不要消费者, 超时后, 消息自动发到死信队列, 由那边的消费者消费, 转了个弯实现了延时队列的效果 -->
<!-- 测试方法: 给queue-ttl-10-seconds发送消息, 10秒后, queue-delay的消费者受到该消息 -->
<rabbit:queue id="queue-ttl-10-seconds" name="queue-ttl-10-seconds" auto-declare="true" durable="false">
    <rabbit:queue-arguments>
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
        <!-- 该队列中, 如果消息过期也没被消费, 则把消息扔到exchange-delay交换机, 使用的routingKey是delay.letter -->
        <entry key="x-dead-letter-exchange" value="exchange-delay"/>
        <entry key="x-dead-letter-routing-key" value="delay.letter"/>
    </rabbit:queue-arguments>
</rabbit:queue>
<rabbit:queue id="queue-delay" name="queue-delay" auto-declare="true" durable="false"/>
<rabbit:direct-exchange id="exchange-delay" name="exchange-delay">
    <rabbit:bindings>
        <rabbit:binding queue="queue-delay" key="delay.letter"/>
    </rabbit:bindings>
</rabbit:direct-exchange>

生产者测试

package com.mrathena.advance;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer-advance.xml")
public class DelayTest {

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Test
	public void testDelay() {
		rabbitTemplate.convertAndSend("", "queue-ttl-10-seconds", "delay - " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值