RabbitMQ

基于amqp协议

四种场景

  • 异步处理

用户注册后,需要发送邮件和短信,两种方式:

1.串行:一个一个发

2.并行:两个同时发

  • 应用解耦

将订单与库存的解耦

  • 流量削峰

用在秒杀活动

  • 消息通信

五种队列

配置

//连接的主机
 spring.rabbitmq.host=8.130.102.114
 //端口
 spring.rabbitmq.port=5672
 //用户名
 spring.rabbitmq.username=admin
 //密码
 spring.rabbitmq.password=admin
 spring.rabbitmq.virtual-host=/
 
  1. helloword

默认交换机 一个生产者发送消息到队列一个消费者接收

  • 生产者
//生产者创建队列 消费者也可
 @Configuration
 public class RabbitMQConfig {
     @Bean
     public Queue queue(){
         Queue queue = new Queue("spb-hello");
         return  queue;
     }
 }
  • 消费者
//消费者消费消息   
 @Component
 public class Listen {
 
     //监听的消息队列
     @RabbitListener(queues = "spb-hello")
     public void one(String msg){
         System.out.println(msg);
     }
  }
  1. Work queue

默认交换机 一个生产者发送消息到队列多个消费者接收(抢)

  • 生产者
同上
  • 消费者
同上两个
  1. Publish

一个生产者发送到交换机 交换机绑定队列 队列给多个消费者推送消息(同时)

  • 生产者
@Configuration
 public class PublishConfig {
     //第一个队列
     @Bean("sms-queue")
     public Queue smsQueue(){
         return new Queue("publish-sms-queue");
     }
     //第二个队列
     @Bean("email-queue")
     public Queue emailQueue(){
         return new Queue("publish-email-queue");
     }
 
     /**
      * 发布订阅交换机
      * @return
      */
     @Bean
     public FanoutExchange exchange(){
        return new FanoutExchange("sms-email-publish-exchange");
     }
 
     //绑定第一个队列
     @Bean
     Binding bindSmsToExchange(@Qualifier("sms-queue") Queue queue, FanoutExchange fanoutExchange){
      return    BindingBuilder.bind(queue).to(fanoutExchange);
     }
     //绑定第二个队列
     @Bean
     Binding bindEmailToExchange(@Qualifier("email-queue") Queue queue, FanoutExchange fanoutExchange){
         return    BindingBuilder.bind(queue).to(fanoutExchange);
     }
 
 }
 
  • 消费者
同上监听队列得到信息
  1. Routing

一个生产者发送到交换机 交换机绑定队列 队列给多个消费者推送消息 通过关键词来判断队那个队列接收

  • 生产者
@Configuration
 public class RoutingConfig {
 //第一个队列
 @Bean("smsqueue")
     public Queue smsqueue(){
     return new Queue("routing-sms-queue");
 }
     //第二个队列
     @Bean("emailqueue")
     public Queue emailqueue(){
         return new Queue("routing-email-queue");
     }
     //交换机
     @Bean
     public DirectExchange exchange(){
     return new DirectExchange("routing-email-sms-diret");
     }
     //绑定第一个队列设置关键词
     @Bean
     Binding smsbinding(@Qualifier("smsqueue") Queue queue, DirectExchange exchange){
     return BindingBuilder.bind(queue).to(exchange).with("sms");
     }
     //绑定第二个队列设置关键词
     @Bean
     Binding emailbinding(@Qualifier("emailqueue") Queue queue, DirectExchange exchange){
         return BindingBuilder.bind(queue).to(exchange).with("email");
     }
     //同时绑定第一个第二个队列设置关键词
     @Bean
     Binding smsemailbinding(@Qualifier("smsqueue") Queue queue, DirectExchange exchange){
         return BindingBuilder.bind(queue).to(exchange).with("sms.email");
     }
     @Bean
     Binding emailsmsbinding(@Qualifier("emailqueue") Queue queue, DirectExchange exchange){
         return BindingBuilder.bind(queue).to(exchange).with("sms.email");
     }
 }
 
 
     //发消息通过关键字给指定队列发送
    @Test
     public void  producerPublish(){
         rabbitTemplate.convertAndSend("sms-email-publish-exchange","sms","这是短信服务");
     }
 
  • 消费者
同上
  1. Topics

一个生产者发送到交换机 交换机绑定队列 队列给多个消费者推送消息 通过关键词来判断队那个队列接收

关键词改变

'*' 代表一个单词

'#' 代表 0个 或 多个 单词

  • 生产者
@Configuration
 public class TopicConfig {
     //队列1
     @Bean("smsqueue")
     public Queue smsqueue(){
         return new Queue("topic-sms-queue");
     }
     //队列2
     @Bean("emailqueue")
     public Queue emailqueue(){
         return new Queue("topic-email-queue");
     }
     //交换机
     @Bean
     public TopicExchange exchange(){
         return new TopicExchange("topic-sms-email-topic");
     }
     //绑定队列1
     @Bean
     Binding smsbind(@Qualifier("smsqueue") Queue queue, TopicExchange exchange){
         return BindingBuilder.bind(queue).to(exchange).with("sms.#");
     }
     //绑定队列二
     @Bean
     Binding emailbind(@Qualifier("emailqueue") Queue queue, TopicExchange exchange){
         return BindingBuilder.bind(queue).to(exchange).with("#.email");
     }
     
     
     //发消息
     //同时绑定只需发消息时同时满足两个队列的关键词
     @Test
     public void  producerTopic2(){
         rabbitTemplate.convertAndSend("topic-sms-email-topic","sms.email","这是短信+邮箱+topic");
     }
 }
 
  • 消费者
同上

消息确认机制

  1. 交换机确认
继承RabbitTemplate.ConfirmCallback类重写以下方法
  @Override
     public void confirm(CorrelationData correlationData, boolean b, String s) {
         System.out.println("发送的消息"+correlationData);
         System.out.println("是否成功"+b);
         System.out.println("错误原因"+s);
         String id = correlationData.getId();
         }
    一般错误都是交换机名字错误
  1. 队列确认
继承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);
         }
   一般错误都是路由键错误
 如果监听到消息 在使用时出现异常不算消费

延迟队列

发送消息给死信队列设置时间 时间一到给正常的交换价发信息

//创建死信队列
 @Bean("dead")
     public Queue deadqueue(){
         Map map = new HashMap();
         map.put("x-dead-letter-exchange","normal-exchange");
         map.put("x-dead-letter-routing-key","normal");
         /**
          1、name: 队列的名称;
          2、durable: 是否持久化;
          3、exclusive: 是否独享、排外的;
          4、autoDelete: 是否自动删除;
          5.arguments:队列的其他属性参数,
          (1)x-message-ttl:消息的过期时间,单位:毫秒;
          (2)x-expires:队列过期时间,队列在多长时间未被访问将被删除,单位:毫秒;
          (3)x-max-length:队列最大长度,超过该最大值,则将从队列头部开始删除消息;
          (4)x-max-length-bytes:队列消息内容占用最大空间,受限于内存大小,超过该阈值则从队列头部开始删除消息;
          (5)x-overflow:设置队列溢出行为。这决定了当达到队列的最大长度时消息会发生什么。有效值是drop-head、reject-publish或reject-publish-dlx。仲裁队列类型仅支持drop-head;
          (6)x-dead-letter-exchange:死信交换器名称,过期或被删除(因队列长度超长或因空间超出阈值)的消息可指定发送到该交换器中;
          (7)x-dead-letter-routing-key:死信消息路由键,在消息发送到死信交换器时会使用该路由键,如果不设置,则使用消息的原来的路由键值
          (8)x-single-active-consumer:表示队列是否是单一活动消费者,true时,注册的消费组内只有一个消费者消费消息,其他被忽略,false时消息循环分发给所有消费者(默认false)
          (9)x-max-priority:队列要支持的最大优先级数;如果未设置,队列将不支持消息优先级;
          (10)x-queue-mode(Lazy mode):将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;如果未设置,队列将保留内存缓存以尽可能快地传递消息;
          (11)x-queue-master-locator:在集群模式下设置镜像队列的主节点信息。
          */
         return new Queue("dead",true,true,true,map);
     }
  
  @RequestMapping("/send/{msg}/{key}")
  //msg消息体key路由键
     public String test3(@PathVariable("msg")String msg,@PathVariable("key")String key){
         //唯一标识
         String uuid = UUID.randomUUID().toString();
         CorrelationData correlationData = new CorrelationData();
         correlationData.setId(uuid);
         //发给死信队列
         rabbitTemplate.convertAndSend("dead-exchange", key, msg, new MessagePostProcessor() {
             //设置时间
             @Override
             public Message postProcessMessage(Message message) throws AmqpException {
                 message.getMessageProperties().setExpiration("10000");
                 return message;
             }
         },correlationData);       
         return "ok";
 
     }

手动ACK

  1. 配置
spring.rabbitmq.listener.simple.acknowledge-mode = manual
  1. 使用
@RabbitHandler
 public void processMessage2(String message, Channel channel,@Headers Map<String,Object> map) {
     System.out.println(message);
     if (map.get("error")!= null){
         System.out.println("错误的消息");
         try {
             channel.basicNack((Long)map.get(AmqpHeaders.DELIVERY_TAG),false,true);      //否认消息
             return;
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     try {
         channel.basicAck((Long)map.get(AmqpHeaders.DELIVERY_TAG),false);            //手动确认消息
     } catch (IOException e) {
         e.printStackTrace();
     }
 }