1、环境搭建
(1)pom依赖:要使用RabbitMQ,只需要额外如下依赖
org.springframework.boot
spring-boot-starter-amqp
(2)application.properties文件
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
(3)三步走
a、配置队列Queue、交换机Exchange和路由键等
b、发送消息
c、监听接收消息
2、direct:点对点,直接发送和监听
a、配置队列Queue、交换机Exchange和路由键等(由于点对点模式没有交换机,所以此处仅仅定义队列即可,不需要配置交换机和路由键)
@Configuration
public class MQConfig {
public static final String QUEUE = "queue";
/**
* Direct模式 交换机Exchange
* */
@Bean
public Queue queue() {
return new Queue(QUEUE, true);
}
}
b、发送消息(直接往队列中发送消息,convertAndSend是个重载方法,第一个参数可以是队列类型比如点对点直接发送,也可以是交换机类型比如fanout模式和topic模式)
@Service
public class MQSender {
@Autowired
RabbitTemplate rabbitTemplate ;
// direct模式
public void sendDirect(String str) {
// 往队列MQConfig.QUEUE发送消息str
rabbitTemplate.convertAndSend(MQConfig.QUEUE, str);
}
}
c、监听接收消息(注解@RabbitListener
)
@Service
public class MQReceiver {
// 监听队列MQConfig.QUEUE,打印接收的消息
@RabbitListener(queues=MQConfig.QUEUE)
public void receive(String message) {
log.info("receive message:"+message);
}
}
3、topic:按照路由键与发送消息时的匹配关系进行转发
a、配置队列Queue、交换机Exchange和路由键等(此处配置了两个队列和一个topic模式的交换机)
@Configuration
public class MQConfig {
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String TOPIC_EXCHANGE = "topicExchage";
/**
* Topic模式 此次配置了两个队列和一个交换机Exchange,两个队列以不同的绑定键绑定到了此交换机上
* */
// 队列1
@Bean
public Queue topicQueue1() {
return new Queue(TOPIC_QUEUE1, true);
}
// 队列2
@Bean
public Queue topicQueue2() {
return new Queue(TOPIC_QUEUE2, true);
}
// topic交换机
@Bean
public TopicExchange topicExchage(){
return new TopicExchange(TOPIC_EXCHANGE);
}
// 绑定1,把队列1与topic交换机绑定,绑定键是topic.key1
@Bean
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchage()).with("topic.key1");
}
// 绑定2,把队列2与topic交换机绑定,绑定键是topic.#
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchage()).with("topic.#");
}
}
b、发送消息(topic和fanout模式中往exchange交换机中发送消息,不是对列)
@Service
public class MQSender {
@Autowired
RabbitTemplate rabbitTemplate ;
// topic模式
public void sendTopic(String str) {
log.info("send topic message:"+str);
rabbitTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", str+"1");
rabbitTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", str+"2");
// 可以知道,队列2可以接收到两个消息,但是队列1只能接收到消息1
}
}
c、监听接收消息(监听的是队列)
@Service
public class MQReceiver {
// 监听队列TOPIC_QUEUE1,打印接收的消息
@RabbitListener(queues=MQConfig.TOPIC_QUEUE1)
public void receiveTopic1(String message) {
log.info(" topic queue1 message:"+message);
}
// 监听队列TOPIC_QUEUE2,打印接收的消息
@RabbitListener(queues=MQConfig.TOPIC_QUEUE2)
public void receiveTopic2(String message) {
log.info(" topic queue2 message:"+message);
}
}
4、fanout:广播发送
a、配置队列Queue、交换机Exchange和路由键等(此处仍然使用上述的两个对列queue1和queue2
)
@Configuration
public class MQConfig {
public static final String FANOUT_EXCHANGE = "fanoutxchage";
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
/**
* Fanout模式 交换机Exchange
* */
// fanout交换机
@Bean
public FanoutExchange fanoutExchage(){
return new FanoutExchange(FANOUT_EXCHANGE);
}
// TOPIC_QUEUE1队列绑定到fanout交换机
@Bean
public Binding FanoutBinding1() {
return BindingBuilder.bind(topicQueue1()).to(fanoutExchage());
}
// TOPIC_QUEUE2队列绑定到fanout交换机
@Bean
public Binding FanoutBinding2() {
return BindingBuilder.bind(topicQueue2()).to(fanoutExchage());
}
}
b、发送消息
@Service
public class MQSender {
@Autowired
RabbitTemplate rabbitTemplate ;
// Fanout模式
public void sendFanout(String str) {
log.info("send fanout message:"+str);
rabbitTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", str);
}
}
c、监听接收消息
@Service
public class MQReceiver {
// 监听队列MQConfig.TOPIC_QUEUE1,打印接收的消息
@RabbitListener(queues=MQConfig.TOPIC_QUEUE1)
public void receiveTopic1(String message) {
log.info(" topic queue1 message:"+message);
}
// 监听队列MQConfig.TOPIC_QUEUE2,打印接收的消息
@RabbitListener(queues=MQConfig.TOPIC_QUEUE2)
public void receiveTopic2(String message) {
log.info(" topic queue2 message:"+message);
}
}
5、死信队列
a、配置队列Queue、交换机Exchange和路由键等
@Configuration
public class MQConfig {
// 死信交换机(在定义产生死信消息的队列时使用此参数),固定写法,不可改变
// DEAD_LETTER_EXCHANGE: 当此普通队列中存在死信时,把死信消息转发到的死信交换机
private static final String DEAD_LETTER_EXCHANGE = "x-dead-letter-exchange";
// 死信交换机绑定(在定义产生死信消息的队列时使用此参数),固定写法,不可改变
// DEAD_LETTER_EXCHANGE:死信消息被转发到死信交换机后,将按照此路由键转发到死信队列)
private static final String DEAD_LETTER_ROUTING_KEY = "x-dead-letter-routing-key";
// 普通队列(由于此队列可能产生死信消息,所以使用DEAD_LETTER_EXCHANGE和DEAD_LETTER_EXCHANGE参数
@Bean
public Queue normalQueue() {
Map args = new HashMap(2);
args.put(DEAD_LETTER_EXCHANGE, "DEAD_LETTER_EXCHANGE");
args.put(DEAD_LETTER_ROUTING_KEY, "REDIRECT_KEY");
// 当产生死信消息时,把死信消息转发到死信交换机DEAD_LETTER_EXCHANGE中,死信交换机再通过路由REDIRECT_KEY转发到死信队列
return new Queue("NORMAL_QUEUE", true, false, false, args);
}
// 死信队列
@Bean
public Queue deadLetterQueue() {
return new Queue("DEAD_LETTER_QUEUE", true);
}
// 普通交换机
@Bean
public Exchange normalExchange() {
//return ExchangeBuilder.directExchange("NORMAL_EXCHANGE").durable(true).build();
return new DirectExchange("NORMAL_EXCHANGE");
}
// 死信交换机
@Bean
public TopicExchange deadLetterExchange() {
return new TopicExchange("DEAD_LETTER_EXCHANGE");
}
// 普通队列与普通交换机绑定
@Bean
public Binding deadLetterBinding() {
return new Binding(
"NORMAL_QUEUE",
Binding.DestinationType.QUEUE,
"NORMAL_EXCHANGE",
"NORMAL_KEY", null);
}
// 死信队列与死信交换机绑定
@Bean
public Binding redirectBinding() {
return new Binding(
"DEAD_LETTER_QUEUE",
Binding.DestinationType.QUEUE,
"DEAD_LETTER_EXCHANGE",
"REDIRECT_KEY", null);
}
}
b、发送消息
@Service
public class MQSender {
@Autowired
RabbitTemplate rabbitTemplate ;
public void sendOrderMessage(final OrderInfo orderInfo){
String msg = RedisService.beanToString(orderInfo);
try {
if (orderInfo!=null){
// 因为普通交换机是NORMAL_EXCHANGE,且普通交换机和正常队列的绑定键是NORMAL_KEY
// 所以此处发送的消息会被路由到普通队列
rabbitTemplate.convertAndSend("NORMAL_EXCHANGE","NORMAL_KEY",msg, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
MessageProperties mp=message.getMessageProperties();
//TODO:动态设置TTL(为了测试方便,暂且设置10s)
mp.setExpiration(String.valueOf(1000*20));
System.out.println("in the sendOrderMessage hs");
return message;
}
});
}
}catch (Exception e){
log.error("秒杀成功后生成抢购订单-发送信息入死信队列,等待着一定时间失效超时未支付的订单-发生异常,消息为:{}",orderInfo.getId(),e.fillInStackTrace());
}
}
}
c、监听接收消息
@Service
public class MQReceiver {
@RabbitListener(queues = "DEAD_LETTER_QUEUE")
public void consumeExpireOrder(String message){
OrderInfo orderInfo = RedisService.stringToBean(message, OrderInfo.class);
System.out.println("in the 死信监听中");
try {
log.info("用户秒杀成功后超时未支付-监听者-接收消息:{}",orderInfo);
if (orderInfo!=null && orderInfo.getStatus().intValue()==0){
orderService.updateStatus(orderInfo.getId());
long goodsId = orderInfo.getGoodsId();
//修改库存
long stock = redisService.incr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);
}
}catch (Exception e){
log.error("用户秒杀成功后超时未支付-监听者-发生异常:",e.fillInStackTrace());
}
}
}