JMS与AMQP的区别
- JMS就是JMS API,主要为Java应用提供统一的消息操作,支持的消息类型有:TextMessage、MapMessage、ByteMessage、StreamMessage、ObjectMessage、Message,ActiveMQ就是基于JMS实现的。
- AMQP是一种协议,可以跨平台使用,并不受客户端/中间件不同产品、不同开发语言的限制,只支持byte[],本质来讲、AMQP的消息模型,在路由机制上做了更详细的划分。
获取MQ的连接
public class ConnectionUtils {
public static Connection getConnection() throws IOException, TimeoutException {
//定义一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("118.31.224.147");
//AMQP 5672
factory.setPort(5672);
//设置虚拟主机 vhost 相当于数据库中的库 一般以 / 开头
factory.setVirtualHost("/");
//用户名
factory.setUsername("guest");
//密码
factory.setPassword("guest");
return factory.newConnection();
}
}
1.1 模型
简单队列
如果发送消息时不绑定任何交换机,只写" ",使用的默认交换机-----AMQP default
P:消息生产者
红色的队列
C:消费者
不足:耦合度高-----不能多个消费者消费队列中消息
工作队列
为什么会出现工作队列?
simple队列是一一对应的,而且我们实际开发,生产者发送消息很快,而消费者一般是要跟业务相结合的,消费者接收到消息后就需要处理,可能会花费时间,这时候队列就会积压很多消息。
默认分配方式是轮询方式,即不管消费者处理信息的效率,队列都会给所有消费者轮流发送一条信息,直到消息发送完毕。
订阅模式
借助fanout 交换机。
交换机并没有存储数据的能力,只有队列有。
路由模式
借助直连交换机,绑定队列时有一个路由key,根据路由key保证发送到哪些消费者上。
主题模式
可以使用通配符来选择发送到哪个队列。
消息接收应答方式
RabbitMQ发送消息给队列,会收到一个应答,然后从RabbitMQ中删除该消息。
默认是两种:
- 一发送到消费者端,就会自动确认就会直接删除队列中的消息
- 手动应答:在合适的时候进行确认消息,删除队列中的消息,如果不回应,这个消息则会一直留在mq里
但是用 spring 整合 mq 时,有三种设置:
- manual:手动 ack,需要在业务代码结束后,调用 api 发送 ack
- auto:自动 ack,由 spring 监测 listener 代码是否出现异常,没有异常则返回 ack,反之返回 nack,如果出现异常会一直重试
- none:关闭 ack,MQ 在消息投递后会立即删除消息
//自动应答:表示消费者一收到消息就会提示队列中可以删除该消息,不管你对消息的操作是否完成
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
//当消费者连上队列之后,消费者没有指定一次获取消息的条数,所以队列把消息一下子推送到消费者端,然后自动确认就会直接删除队列中的消息
//手动应答:在合适的时候进行确认消息,删除队列中的消息,如果不回应,这个消息则会一直留在mq里
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
//通过显示调用 channel.basicAck(envelope.getDeliveryTag(), false); 来告诉MQ删除消息
//信息执行失败 最后一个参数为true,会把该消息重新放到队列的末端,为false则不会重回队列
//第二个参数为是否是批量的
channel.basicNack(envelope.getDeliveryTag(), false, true);
如果设置消费者每次从队列中获取指定的条数
channel.basicQos(1);
如果开启手动应答,消费者将不再接收消息 这时候就是公平分发,能者多劳。
持久化
//声明队列时设置队列持久化 durable默认是false 未持久化
channel.queueDeclare(QUEUE_NAME, durable, false, false, null);
//如果MQ中已经有一个同名的队列,再更改durable未true是不可以的,rabbitMQ不允许重新定义一个已存在的队列
//交换器持久化 durable=true
channel.exchangeDeclare(EXCHANGE_NAME,"direct",true);
rabbitMQ的消息发送确认机制(事务 + confirm)
在rabbitMQ中,我们可以通过持久化数据,解决rabbitMQ服务器异常的数据丢失问题。
生产者将消息发送出去之后,消息到底到没到rabbitMQ服务器,怎么知道到没到呢?
两种方式
AMQP实现了事务机制
try {
//开启事务
channel.txSelect();
//发布消息 basic:基础的 publish:发布
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
//提交
channel.txCommit();
} catch (Exception e) {
//回滚数据
channel.txRollback();
}
Confirm模式 实现原理
confirm只保证发送到交换机,路由到队列是否成功需要 returnListener 即 ReturnCallback。
生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列后,broker就会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者得知消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息重新写入磁盘后发出,broker回传给生产者的确认消息中,deliver-tag域包含了确认消息的序列号,此外broker也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。
confirm是异步的,
普通方式确认:
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//从连接中获取一个信道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将channel设置为confirm模式 如果该队列已经开启事务,则不能再开启confirm模式
channel.confirmSelect();
String msg = "heool confirm message";
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
//普通发送方式确认
if (!channel.waitForConfirms()) {
System.out.println("发送失败 message send failed");
} else {
System.out.println("发送成功 message send ok");
}
channel.close();
connection.close();
}
批量发送确认:
channel.waitForConfirmsOrDie()批量确认模式;
异步模式:
Channel 对象提供的 ConfirmListener() 回调方法只包含 deliveryTag (当前 Chanel 发出的消息序列号),我们需要为每一个 Channel 维护一个 unconfirm 的消息序列集合,每 publish 一条数据,集合中元素加 1,每回调一次 handleAck 方法,unconfitrm 集合删除响应的一条 (multiple = false)或多条(multiple = true)记录,从程序上看,这个 unconfirm 最好采用有序集合 SortedSet 存储数据。
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//从连接中获取一个信道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将channel设置为confirm模式 如果该队列已经开启事务,则不能再开启confirm模式
channel.confirmSelect();
SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
channel.addConfirmListener(new ConfirmListener() {
@Override
//b为true 一下删除多条记录 没问题调用的方法
public void handleAck(long l, boolean b) throws IOException {
System.out.println("已确认消息:" + l + ",是否是多个消息:" + b);
if (b) {
//删除l + L之前的所有数据
confirmSet.headSet(1 + l).clear();
} else {
confirmSet.remove(l);
}
}
//有问题调用的方法
@Override
public void handleNack(long l, boolean b) throws IOException {
System.out.println("已确认消息失败:" + l + ",是否是多个消息:" + b);
if (b) {
confirmSet.headSet(1 + l).clear();
} else {
confirmSet.remove(l);
}
}
});
String msg = "hello confirm message";
for (int i = 0; i < 50; i++) {
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
long nextPublishSeqNo = channel.getNextPublishSeqNo();
System.out.println(nextPublishSeqNo + " 条消息");
confirmSet.add(nextPublishSeqNo);
}
// channel.close(); 不能关闭 因为需要处理异步的消息确认
// connection.close();
}
spring 集成 RabbitMQ
依赖
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
</dependencies>
xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd" >
<!-- 定义rabbitMQ的连接工厂-->
<rabbit:connection-factory id="connectionFactory"
host="118.31.224.147" port="5672" username="test_vhost" password="654321"
virtual-host="/test_vhost" />
<!-- 定义Rabbit模板,指定连接工程以及定义exchange,也可定义队列-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="fanoutExchange"/> <!--queue=""-->
<!-- NQ的管理,包括队列、交换器声明等-->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 定义队列 durable=true:开启持久化默认是true-->
<rabbit:queue name="myQueue" durable="true"/>
<!-- 定义订阅模式交换器,自动声明-->
<rabbit:fanout-exchange name="fanoutExchange" durable="true">
<rabbit:bindings>
<rabbit:binding queue="myQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- 路由模式 <rabbit:direct-exchange name="directExchange"/>-->
<!-- 主题模式 <rabbit:topic-exchange name="topicExchange"/>-->
<!-- 队列监听-->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen" queue-names="myQueue"/>
</rabbit:listener-container>
<!-- 消费者-->
<bean id="foo" class="com.wzl.rabbitmq.spring.MyConsumer"/>
</beans>
注解配置类
@Configuration
@ComponentScan({"com.wzl.rabbitmq.spring"})
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("118.31.224.147");
connectionFactory.setUsername("test_vhost");
connectionFactory.setPassword("654321");
connectionFactory.setVirtualHost("/test_vhost");
return connectionFactory;
}
/**MQ的管理,也可对队列,交换机等的操作
* <rabbit:admin connection-factory="connectionFactory"/>*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//这里要设置成自动启动
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/**rabbitMQ模板
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="fanoutExchange"/>*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
//可以通过rabbitTemplate开启confirm模式等操作
return rabbitTemplate;
}
/**
* 监听容器,容器启动之后就可以持续监听
*/
@Bean
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//监听队列
container.setQueueNames("test_direct_queue");
//当前的消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//是否使用重回队列:否
container.setDefaultRequeueRejected(false);
//应答方式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//监听到之后的操作
container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
String msg = new String(message.getBody());
System.out.println("------------------消费者监听到了消息:" + msg);
}
});
//也可用适配器方式进行监听操作
MessageListenerAdapter adapter = new MessageListenerAdapter(new ListenerAdapter());
//默认监听方法名是handleMessage,修改为listenerMethod
adapter.setDefaultListenerMethod("listenerMethod");
//适配器方法:让队列名称和方法名进行一一对应
Map<String,String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queueName","methodName");
adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
container.setMessageListener(adapter);
return container;
}
监听器类
/**
* 如果没有接收对应参数类型的方法,会报异常
* 或者监听容器设置一个实现MessageConverter接口的转换器
*/
public class ListenerAdapter {
/**监听字节数组为参数的消息的操作*/
public void handleMessage(byte[] message) {
System.out.println("适配器的默认方法,消息内容" + new String(message));
}
/**监听字符串为参数的消息的操作*/
public void handleMessage(String message) {
System.out.println("适配器的默认方法,消息内容" + message);
}
}
声明、绑定与发送消息
//声明与绑定方式:
public class RabbitTests {
private static RabbitAdmin rabbitAdmin;
private static RabbitTemplate rabbitTemplate;
static {
ApplicationContext context = new AnnotationConfigApplicationContext(RabbitMqConfig.class);
rabbitAdmin = (RabbitAdmin) context.getBean("rabbitAdmin");
rabbitTemplate = (RabbitTemplate) context.getBean("rabbitTemplate");
}
@Test
public void testAdmin() {
//声明创建交换机,不持久化
rabbitAdmin.declareExchange(new DirectExchange("test_direct", false, false));
//声明创建队列,不持久化
rabbitAdmin.declareQueue(new Queue("test_direct_queue", false));
//绑定交换机
rabbitAdmin.declareBinding(new Binding("test_direct_queue", //队列名
Binding.DestinationType.QUEUE,//队列绑定
"test_direct", //交换机名
"routingKey", //路由名
null)); //map集合
//清空队列数据 false是否需要等待
rabbitAdmin.purgeQueue("queueName",false);
}
//发送消息
@Test
public void testSendMessage() {
//RabbitMq的消息主要是两部分组成 一部分是properties,和要发送的消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put("desc", "信息描述...");
messageProperties.getHeaders().put("headers","自定义的数据");
Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
rabbitTemplate.convertAndSend("test_direct", "routingKey", message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
System.out.println("消息后置处理操作,先对消息进行处理操作,再发送");
message.getMessageProperties().getHeaders().put("desc","修改的信息描述...");
return message;
}
});
//使用的是test_direct交换机
rabbitTemplate.send("test_direct","routingKey",message);
//直接向指定队列发送消息 使用的是默认的交换机------AMQP default
rabbitTemplate.convertAndSend("test_direct_queue", "hello Java");
}
}
生产者
public class SpringProducer {
public static void main(String[] args) {
AbstractApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:rabbitMq.xml");
//RabbitMQ模板
RabbitTemplate template = classPathXmlApplicationContext.getBean(RabbitTemplate.class);
//发送消息
template.convertAndSend("Hello World!");
try {
Thread.sleep(1000);
classPathXmlApplicationContext.destroy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
配置文件的消费者
public class MyConsumer {
public void listen(String msg) {
System.out.println("消费者:" + msg);
}
}
springBoot 集成 RabbitMQ
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
spring.rabbitmq.host=118.31.224.147
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#消息确认改为手动 开启ACK simple 和 direct 是rabbitmq 的容器类型
spring.rabbitmq.listener.type=simple
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.direct.acknowledge-mode=manual
# 消费者每次只取用一个消息
spring.rabbitmq.listener.simple.prefetch=1
spring.rabbitmq.listener.direct.prefetch=1
# 开启confirm发送确认机制
# 通过实现ConfirmCallBack接口,消息发送到交换器Exchange后触发回调
spring.rabbitmq.publisher-confirm-type=correlated
#通过实现ReturnCallback接口,如果消息从交换器发送到对应队列失败时触发
# 比如根据发送消息时指定的routingKey找不到队列时会触发
spring.rabbitmq.publisher-returns=true
#设置为 true 后 消费者在消息没有被路由到合适队列情况下会被return监听,
# 而不会自动删除
spring.rabbitmq.template.mandatory=true
配置类
比spring整合RabbitMQ更简单,不需要创建Admin、RabbitTemplate和监听容器,大部分配置可以在配置文件中配置
@Configuration
public class FanoutConfig {
/**两个队列*/
/**this(name, true, false, false);
durable:默认开启持久化 exclusive:与mq的连接断开时该队列是否自动删除 autoDelete:最后一个消费者断 开连接之后队列是否自动删除*/
@Bean
public Queue fanout1() {
return new Queue("fanout1");
}
@Bean
public Queue fanout2() {
return new Queue("fanout2");
}
/**一个交换机*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
}
/**把队列绑定到交换机,后还可以.with(routingKey路由键)*/
@Bean
public Binding fanoutQueue1() {
return BindingBuilder.bind(fanout1()).to(fanoutExchange());
}
@Bean
public Binding fanoutQueue2() {
return BindingBuilder.bind(fanout2()).to(fanoutExchange());
}
}
设置某些队列手动 ack
设置某些队列手动 ack 需要新建一个监听容器
@Configuration
public class WordMqConfiguration {
public static final String INFORM_USER_RECITE_WORD_EXCHANGE="user.recite.word.exchange";
public static final String USER_RECITE_WORD_QUEUE_RK="user.recite.word.queue.rk.*";
@Bean
public Exchange informUserReciteWordExchange(){
return ExchangeBuilder.topicExchange(INFORM_USER_RECITE_WORD_EXCHANGE).durable(true).build();
}
@Bean
public Queue informUserReciteWordQueue() {
return new Queue(MqConstants.SAVE_USER_RECITE_WORD_QUEUE);
}
@Bean
public Binding informUserReciteWordBinding(@Qualifier("informUserReciteWordQueue") Queue queue,
@Qualifier("informUserReciteWordExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(USER_RECITE_WORD_QUEUE_RK).noargs();
}
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 设置确认模式手工确认
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setQueues(informUserReciteWordQueue());
return container;
}
}
发送与消费消息
生产者
@Component
public class RabbitSend {
@Autowired
private RabbitTemplate rabbitTemplate;
/**确保消息正确发送到交换机*/
private RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("消息唯一标识:" + correlationData);
System.out.println("消息是否发送成功:" + b);
if (b) {
System.out.println("发送成功");
} else {
System.err.println("发送失败,失败原因:" + s);
}
}
};
/**确保消息正确发送到队列,发送到队列时失败之后的操作*/
private RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
System.out.println("消息主题:" + message);
System.out.println("响应码:" + i);
System.out.println("错误概述:" + s);
System.out.println("使用的交换机:" + s1);
System.out.println("消息使用的路由键:" + s2);
}
};
public void send() {
//开启confirm模式,防止消息发送到交换机出现问题
rabbitTemplate.setConfirmCallback(confirmCallback);
//开启return机制,防止消息发送到队列出现问题
rabbitTemplate.setReturnCallback(returnCallback);
rabbitTemplate.convertAndSend("test_direct_queue", "hello confirm".getBytes());
}
}
消费者
@Component
@RabbitListener(queues = "test_direct_queue")
public class RabbitMessageListener {
@RabbitHandler
//发送消息时发送什么类型的数据,这里就需要一个什么类型的数据接收,一般用字符串类型,发送对象需要序列化
public void message(Channel channel, Message message,String msg) throws IOException {
System.out.println("----------------------------------------------");
System.out.println(message);
System.out.println("接收到消息:" + msg);
//设置了手动应答
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//信息执行失败 ,最后一个参数为true,会把该消息重新放到队列的末端,为false则不会重回队列第二个为消息是否是批量的
//channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
补偿机制
SpringBoot整合-RabbitMQ,消费者消费过程中出现异常,会自动进行重试,可以修改重试次数与重试间隔时间。实现原理是AOP捕获到异常就缓存到RabbitMq服务端进行重试。
死信/死信队列
什么是死信?
- 消息被拒绝(basic.reject或者basic.nack)并且requeue=false
- 消息TTL过期
- 队列达到最大长度,队列满了
死信的处理方式
- 丢弃,如果不是很重要,可以选择丢弃
- 记录死信入库,然后做后续的业务分析或处理
- 通过死信队列,由负责监听死信的应用程序进行处理
public class DLXRecv {
public static final String QUEUE_NAME = "test_dlx_queue";
public static final String EXCHANGE_NAME = "test_dlx_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//声明、创建普通的交换机 主题模式
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
Map<String, Object> arguments = new HashMap<>();
/*value是自己设定的*/
arguments.put("x-dead-letter-exchange", "dlx.exchange");
// 设置队列中的消息 10s 钟后过期
arguments.put("x-message-ttl", 10000);
/*arguments当做声明队列的参数,声明正常的队列,出现死信后消息进入指定的死信交换机*/
channel.queueDeclare(QUEUE_NAME, true, false, false, arguments);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "dlx.#");
/*创建、声明死信交换机,交换机名字是前面map里存的value,然后绑定队列、该队列就成了死 信队列*/
channel.exchangeDeclare("dlx.exchange","topic",true);
channel.queueDeclare("dlx.queue",true,false,false,null);
//绑定队列到交换机 路由key是#,所有都可以接收
channel.queueBind("dlx.queue","dlx.exchange","#");
/*这是收到消息要做的处理*/
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "utf-8");
System.out.println(msg);
//手动回执 告诉rabbitMQ可以删除这条消息了
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
/*false:手动确认 在合适的时候来进行消息确认
* 监听的是"dlx.queue",如果消费者收到消息,说明死信队列有消息了*/
channel.basicConsume("dlx.queue", false, consumer);
}
}
springBoot创建死信队列
@Configuration
public class RabbitMqConfig {
@Bean
public Queue orderQueue() {
Map<String, Object> arguments = new HashMap<>(2);
arguments.put("x-dead-letter-exchange","dlx_exchange");
arguments.put("x-message-ttl",10000);
return new Queue("no_dlx_queue",true,false,false,arguments);
}
@Bean
public Queue dlxQueue() {
return new Queue("dlx_queue");
}
@Bean
public TopicExchange dlxExchange() {
return new TopicExchange("dlx_exchange");
}
//队列绑定死信交换机,称为死信队列
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("#");
}
}
信道Channel的作用
数据流动都是通过channel进行的,而且定义Queue、Exchange、绑定Queue和Exchange、发布消息、监听队列等都是通过Channel上进行的