SpringBoot整合RabbitMQ实战案例

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());
		}
	}
}

 

 

你可能感兴趣的:(中间件)