工作中大概用到,想着以后可能还会使用,所以还是需要记录下。网上的资料确实很多,也很全,毕竟适合自己的也不多,从优秀的里面汲取自己需要的,才是正道。
看了很多博客,文档啊,还有操作指南啥的,我也总结下对我来说简单明了的。概念啥的慢慢再补充,先把简单的操作使用记录下。
项目是springboot跟rabbitMq集成的
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
spring:
rabbitmq:
host: 192.168.3.95
port: 5672
username: admin
password: admin
virtual-host: my_vhost
listener:
direct:
acknowledge-mode: manual #开启消息确认机制
simple:
acknowledge-mode: manual #开启消息确认机制
生产者代码
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* exchange 交换机名称 就是下面图形化配置或者代码配置的交换机名称
* routKey 路由key 交换机中队列绑定的路由key
* msgId 消息id,唯一标识
* msg 消息
*/
@Override
public boolean sendDirectMessage(String exchange, String routKey, String msgId, String msg) {
rabbitTemplate.convertAndSend(exchange, routKey, msg, message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); //持久化
message.getMessageProperties().setExpiration("10000"); //消息过期时间
return message;
}, new CorrelationData(msgId));
return true;
}
消费者
@RabbitListener(queues = "hand_cyc_queue") //监听的队列名称
public void handListener(String msg, Channel channel, Message message) {
try {
log.info("消息{}", msg);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
} catch (Exception e) {
e.printStackTrace();
//丢弃这条消息
try {
//basicReject 和 basicNack区别就是可以多条和单条的区别
//最后一个参数是:是否重回队列 true重回队列 false不回队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
} catch (IOException e1) {
e1.printStackTrace();
}
log.info("消息消费失败:id:{}", message.getMessageProperties().getDeliveryTag());
}
}
使用方式(都是direct方式记录的):
1.使用客户端界面添加。也是最简单直观的方式。
(1)创建交换机
(2)创建队列
(3)交换机绑定队列和路由key
到这里其实队列,交换机,还有路由key已经都创建绑定成功,使用的话则直接在生产者和消费者分别发送和监听即可。
如果想绑定死信队列的话
<1>死信队列其实就是简单的队列,简单来说 就是普通的队列中取了个不同的名字,稍微区分开了而已。先重复上面的队列,交换机,路由key创建,绑定的操作
<2>再创建一个队列,为正常业务的队列,创建绑定,交换机,队列,路由key操作,然后给这个队列添加一些特别的属性,意思该队列中消息消费异常,过期等一些消息路由到刚刚创建的死信队列上
这样针对死信队列的界面话操作就完成了,如果想消费,就和正常的消费者一样。只需要在@RabbitListener(queues = “死信队列名称”)
2.代码实现
rabbitmq的properties配置
#direct模式 正常业务的死信队列,交换机,路由key的相关配置
rabbit.client.direct_exchange=auto_direct_cyc_exchange
rabbit.client.direct_queue=auto_direct_cyc_queue
rabbit.client.direct_routeKey=auto_direct_cyc_routeKey
rabbit.client.fanout_exchange=fanout_exchange
#direct 死信队列,交换机,路由key的相关配置
rabbit.client.dead_direct_exchange=auto_dead_exchange
rabbit.client.dead_direct_queue=auto_dead_queue
rabbit.client.dead_direct_routeKey=auto_dead_routeKey
RabbitProperty配置类
@Component
//自定义配置文件路径
@ConfigurationProperties(prefix = "rabbit.client")
@PropertySource("classpath:rabbit.properties")
@Data
public class RabbitProperty {
private String directExchange;
private String directQueue;
private String directRouteKey;
private String deadDirectExchange;
private String deadDirectQueue;
private String deadDirectRouteKey;
}
正常业务队列,交换机,路由key绑定以及注入
@Configuration
public class RabbitMqConfig {
@Autowired
private RabbitProperty rabbitProperty;
// 创建一个立即消费队列
@Bean
public Queue immediateQueue() {
return QueueBuilder.durable(rabbitProperty.getDirectQueue())
.withArgument("x-dead-letter-exchange", rabbitProperty.getDeadDirectExchange())//设置死信交换机
// .withArgument("x-message-ttl", makeCallExpire)
.withArgument("x-dead-letter-routing-key", rabbitProperty.getDeadDirectRouteKey())//设置死信routingKey
.build();
}
@Bean
public DirectExchange immediateExchange() {
// 一共有三种构造方法,可以只传exchange的名字, 第二种,可以传exchange名字,是否支持持久化,是否可以自动删除,
//第三种在第二种参数上可以增加Map,Map中可以存放自定义exchange中的参数
return new DirectExchange(rabbitProperty.getDirectExchange(), true, false);
}
@Bean
//把立即消费的队列和立即消费的exchange绑定在一起
public Binding immediateBinding() {
return BindingBuilder.bind(immediateQueue()).to(immediateExchange()).with(rabbitProperty.getDirectRouteKey());
}
}
死信队列相关配置
/**
* 死信队列相关配置
* @author cyc
* @date 2021/4/9 11:37
*/
@Configuration
public class DeadConfig {
@Autowired
private RabbitProperty rabbitProperty;
/**
* 死信队列
*
* @return
*/
@Bean
public Queue deadQueue() {
return new Queue(rabbitProperty.getDeadDirectQueue(), true);
}
/**
* 死信队列交换机
*
* @return
*/
@Bean
DirectExchange deadExchange() {
return new DirectExchange(rabbitProperty.getDeadDirectExchange());
}
/**
* 死信队列绑定到交换机对应的路由key上
*
* @return
*/
@Bean
Binding bindingDeadQueue() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(rabbitProperty.getDeadDirectRouteKey());
}
}
生产者
@Service
public class ProducerMessage {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
*
* @param exchange 交换机名称
* @param routKey 路由key
* @param msgId 消息id
* @param msg 消息
* @return
*/
public boolean sendAutoDirectMessage(String exchange, String routKey, String msgId, String msg) {
rabbitTemplate.convertAndSend(exchange, routKey, msg, message -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
message.getMessageProperties().setExpiration("10000"); //消息过期时间
return message;
}, new CorrelationData(msgId));
return true;
}
}
消费者
@Component
@Slf4j
public class ConsumerMessage {
@RabbitListener(queues = "auto_direct_cyc_queue")
public void chyListener(String msg, Channel channel, Message message) {
try {
log.info("消息{}", msg);
SysUser sysUser = JSONObject.parseObject(msg, SysUser.class);
if (sysUser.getSex() == 1) {
//手动ack应答
//告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了
// 否则消息服务器以为这条消息没处理掉 后续还会在发,true确认所有消费者获得的消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("消息消费成功:id:{}", message.getMessageProperties().getDeliveryTag());
} else {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
log.info("消息消费失败:id:{}", message.getMessageProperties().getDeliveryTag());
}
} catch (IOException e) {
e.printStackTrace();
try {
//最后一个参数是:是否重回队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
} catch (IOException e1) {
e1.printStackTrace();
}
log.info("消息消费失败:id:{}", message.getMessageProperties().getDeliveryTag());
}
}
}
至于rabbitmq的各种路由方式,生产消息确认,消息消费手动签收,过期啊,延迟等相关特性以后慢慢的进行更新。