RabbitMq消息队列-看着一篇就够了

什么是 MQ

是基础数据结构中“先进先出”的一种数据结构。一般用来解决 应用解耦异步消息
流量削峰等问题,实现高性能高可用可伸缩和最终一致性架构。#

为什么引入MQ

流量消峰

当我们的系统受到高并发访问,那么势必会造成我们系统的负担加重,从而使我们的系统宕机,但是MQ会让我们在高并发情况下,使用消息队列做缓冲,使某一时刻的处理请求分散开来,减少系统在这一时刻的压力。

应用解耦

在分布式架构中,假如某一子系统出现故障,不会影响到其他系统的操作,比如用户下单请求, 扣减库存,如果库存系统出现故障, 那么用户可以正常下单, 下单的信息放到MQ中, 等到库存系统恢复后, 执行扣减库存操作。不会影响用户下单。

异步处理

A,B两个系统, A调B B需要花很长时间去执行完毕, 使用消息总线, 则A就不需要等待B执行完毕。异步调用处理

RabbitMq名词介绍

在这里插入图片描述

Broker:接收和分发消息的应用,RabbitMQ Server 就是 Message Broker
Virtual host:  出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似
于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,
可以划分出多个vhost, 每个用户在自己的 vhost 创建 exchange/queue 等 
Connection:publisher/consumer 和 broker 之间的 TCP 连接
Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection 的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯,AMQP method 包含了 channel id 帮助客户端和 message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的Connection 极大减少了操作系统建立 TCP connection 的开销 
Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发
消息到 queue 中去。常用的类型有:direct, topic  and fanout 
Queue:消息最终被送到这里等待 consumer 取走
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key,Binding 信息被保
存到 exchange 中的查询表中,用于 message 的分发依据

Linux安装

官网地址

 https://www.rabbitmq.com/download.html

上传到linux服务器中

在这里插入图片描述

安装文件

 rpm -ivh erlang-21.3-1.el7.x86_64.rpm
 yum install socat -y
 rpm -ivh rabbitmq-server-3.8.8-1.el7.noarch.rpm

配置

添加开机启动 RabbitMQ 服务

chkconfig rabbitmq-server on

启动服务

  /sbin/service rabbitmq-server start

查看服务状态

/sbin/service rabbitmq-server status

在这里插入图片描述

停止服务

/sbin/service rabbitmq-server stop

开启 web 管理插件

rabbitmq-plugins enable rabbitmq_management

添加一个新的用户

rabbitmqctl add_user admin 123

设置用户角色

rabbitmqctl set_user_tags admin administrator

设置用户权限

set_permissions [-p <vhostpath>] <user> <conf> <write> <read>
rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"

用户 user_admin 具有/vhost1 这个 virtual host 中所有资源的配置、写、读权限

当前用户和角色

rabbitmqctl list_users

重置命令

关闭应用的命令为

 rabbitmqctl stop_app

清除的命令为

rabbitmqctl reset

重新启动命令为

rabbitmqctl start_app

RabbitMq的使用

配置依赖环境

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leava-cloud</artifactId>
        <groupId>com.leava.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>leava-rabbitmq</artifactId>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--添加Lombok插件依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <optional>true</optional>
        </dependency>

        <!--rabbitmq 依赖客户端-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.8.0</version>
        </dependency>
        <!--操作文件流的一个依赖-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>
</project>

简单消息

package com.leava.cloud.mqTest;

import com.leava.cloud.ApplicationTest;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import org.junit.Before;
import org.junit.Test;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/17 0017
 * 简单消息发送
 */
public class MqApplicationTest1 extends ApplicationTest {

  final ConnectionFactory factory = new ConnectionFactory();

  private final static String QUEUE_NAME = "hello";

  @Before
  public void connectionFactory() {
    //创建一个连接工厂
    factory.setHost("192.168.157.49"); //Ip地址
    factory.setUsername("admin"); //账号
    factory.setPassword("admin"); //密码
  }

  /**
   * 生产者
   */
  @Test
  public void productTest() {
    try {
      Connection connection = factory.newConnection();
      Channel channel = connection.createChannel();
      /**
       * 生成一个队列
       * 1.队列名称
       * 2.队列里面的消息是否持久化 默认消息存储在内存中
       * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
       * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
       * 5.其他参数
       */
      channel.queueDeclare(QUEUE_NAME, false, false, false, null);
      String message = "hello world";
      /**
       * 发送一个消息
       * 1.发送到那个交换机
       * 2.路由的 key 是哪个
       * 3.其他的参数信息
       * 4.发送消息的消息体
       */
      channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
      System.out.println("消息发送完毕");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  /**
   * 消费者
   */
  @Test
  public void consumerTest() {
    try {
      Connection connection = factory.newConnection();
      Channel channel = connection.createChannel();
      System.out.println("等待接收消息....");
      //推送的消息如何进行消费的接口回调
      DeliverCallback deliverCallback = (consumerTag, delivery) -> {
        String message = new String(delivery.getBody());
        System.out.println(message);
      };
      //取消消费的一个回调接口 如在消费的时候队列被删除掉了
      CancelCallback cancelCallback = (consumerTag) -> {
        System.out.println("消息消费被中断");
      };
      /**
       * 消费者消费消息
       * 1.消费哪个队列
       * 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答
       * 3.消费者未成功消费的回调
       */
      channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    } catch (Exception e) {
      System.out.println("消息消费失败");
    }
  }
}

消息分发规则

package com.leava.cloud.mqTest;

import com.leava.cloud.ApplicationTest;
import com.leava.cloud.util.MqConnectionUtil;
import com.leava.cloud.util.SleepUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/17 0017
 * 不公平分发  以及  预期值设置
 */
public class MqApplicationTest31 extends ApplicationTest {
  private static final String ACK_QUEUE_NAME="ack_queue";
  public static void main(String[] args) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    
    //消息消费的时候如何处理消息
    DeliverCallback deliverCallback=(consumerTag, delivery)->{
      String message= new String(delivery.getBody());
      
      System.out.println("接收到消息:"+message);
      /**
       * 1.消息标记 tag
       * 2.是否批量应答未应答消息
       */
      channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
    };
    // TODO: 2021/6/17 0017  prefetchCount 参数可以有 0:轮询策略 1:不公平分发 2.... 之后的都是预取值
    //不公平分发
    //int prefetchCount = 1;
    //预取值
    int prefetchCount = 5;
    channel.basicQos(prefetchCount); //类型为1 就是不公平分发
    //采用手动应答
    boolean autoAck=false;
    channel.basicConsume(ACK_QUEUE_NAME,autoAck,deliverCallback,(consumerTag)->{
      System.out.println(consumerTag+"消费者取消消费接口回调逻辑");
    });
  }
}

channel.basicQos(prefetchCount); 指定分发规则 
  0:就是指 轮询公平分发       默认
  1: 不公平分发,按着每个消费者的能力大小进行分发,能者多劳
 之后的是按着预取值的方式进行分发

确认发布

	     生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的
	消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker
	就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队
	列了,如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传
	给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置
	basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。
	confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信
	道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调
	方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消
	息,生产者应用程序同样可以在回调方法中处理该 nack 消息。  

代码实现

package com.leava.cloud.release;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;

import java.util.UUID;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 * 发布确认
 * 1:单个确认
 * 2:批量确认
 * 3:异步批量确认
 */
public class ReleaseConfirmationOne {

  private final static int MESSAGE_COUNT = 1000;

  public static void main(String[] args) throws Exception {
    // 1:单个确认 速度慢
    //  pushSingle();
    // 2:批量确认  速度快 但是有风险
    // pushBatch();
    // 3:异步批量确认 速度最快 最稳
    pushAsynchronous();
  }


  /**
   * 单个确认
   * 这是一种简单的确认方式,它是一种同步确认发布的方式,也就是发布一个消息之后只有它
   * 被确认发布,后续的消息才能继续发布,waitForConfirmsOrDie(long)这个方法只有在消息被确认
   * 的时候才返回,如果在指定时间范围内这个消息没有被确认那么它将抛出异常。
   * 这种确认方式有一个最大的缺点就是:发布速度特别的慢,因为如果没有确认发布的消息就会
   * 阻塞所有后续消息的发布,这种方式最多提供每秒不超过数百条发布消息的吞吐量。当然对于某
   * 些应用程序来说这可能已经足够了。
   */
  public static void pushSingle() throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    String queryName = UUID.randomUUID().toString();
    channel.queueDeclare(queryName, true, false, false, null);
    channel.confirmSelect();
    long beginTime = System.currentTimeMillis();
    for (int i = 0; i < MESSAGE_COUNT; i++) {
      String message = i + "";
      channel.basicPublish("", queryName, null, message.getBytes());
      boolean flag = channel.waitForConfirms();
      if (flag) {
        System.out.println("消息发送成功");
      }
    }
    long endTime = System.currentTimeMillis();
    System.out.println("单个消息总耗时:" + (endTime - beginTime));
  }

  /**
   * 批量发布
   * 上面那种方式非常慢,与单个等待确认消息相比,先发布一批消息然后一起确认可以极大地
   *   提高吞吐量,当然这种方式的缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现
   *  问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布消息。当然这种
   *  方案仍然是同步的,也一样阻塞消息的发布
   * @throws Exception
   */
  public static void pushBatch() throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    String queryName = UUID.randomUUID().toString();
    channel.queueDeclare(queryName, true, false, false, null);
    channel.confirmSelect();
    int batchSize = 100;
    long beginTime = System.currentTimeMillis();
    for (int i = 0; i < MESSAGE_COUNT; i++) {
      String message = i + "";
      channel.basicPublish("", queryName, null, message.getBytes());
      if (i % batchSize == 0) {
        channel.waitForConfirms();
      }
    }
    long endTime = System.currentTimeMillis();
    System.out.println("批量消息总耗时:" + (endTime - beginTime));
  }

  /**
   * 异步发布确认  todo 推荐
   * 异步确认虽然编程逻辑比上两个要复杂,但是性价比最高,无论是可靠性还是效率都没得说,
     他是利用回调函数来达到消息可靠性传递的,这个中间件也是通过函数回调来保证是否投递成功,
     下面就让我们来详细讲解异步确认是怎么实现的。
   */
  public static void pushAsynchronous() throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    String queryName = UUID.randomUUID().toString();
    channel.queueDeclare(queryName, true, false, false, null);
    channel.confirmSelect();
    long beginTime = System.currentTimeMillis();
    //准备监听器
    ConcurrentSkipListMap<Long, String> outstandingConfirms = new ConcurrentSkipListMap<>(); //并发线程集合
    ConfirmCallback ackCallback = (long deliveryTag, boolean multiple) -> {
      //成功监听
      //删除已经确认的消息 剩下的就是未消费
      if (multiple) {
        //如果是批量
        ConcurrentNavigableMap<Long, String> confirmed = outstandingConfirms.headMap(deliveryTag);
        confirmed.clear();
      } else {
        outstandingConfirms.remove(deliveryTag);
      }
      System.out.println("确认的消息:" + deliveryTag);
    };
    ConfirmCallback nackCallback = (long deliveryTag, boolean multiple) -> {
      //失败监听
      if (multiple) {
        //如果是批量
        ConcurrentNavigableMap<Long, String> noConfirmed = outstandingConfirms.headMap(deliveryTag);
        System.out.println("未确认的消息:" + noConfirmed);
      } else {
        String noConfirmed = outstandingConfirms.get(deliveryTag);
        System.out.println("未确认的消息:" + noConfirmed);
      }
      System.out.println("未确认的消息:" + deliveryTag);
    };
    /**
     * 第一个参数 监听 成功
     * 第二个参数 监听 失败
     */
    channel.addConfirmListener(ackCallback, nackCallback);
    for (int i = 0; i < MESSAGE_COUNT; i++) {
      String message = i + "消息";
      outstandingConfirms.put(channel.getNextPublishSeqNo(), message);
      channel.basicPublish("", queryName, null, message.getBytes());
    }
    long endTime = System.currentTimeMillis();
    System.out.println("异步消息总耗时:" + (endTime - beginTime));
  }
}

交换机

可以看做是消息的中转站点, 用于首次接收和分发消息,其中包括Headers,Fanout ,Direct,Topic 这四种	

绑定

binding 其实是 exchange 和 queue 之间的桥梁,它告诉我们 exchange 和那个队列进行了绑定关系。

在这里插入图片描述

广播 (Fanout)

Fanout 它是将接收到的所有消息广播到它知道的所有队列中。系统中默认有些 exchange 类型

在这里插入图片描述

代码
package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.Scanner;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class EmitLog {
  private static final String EXCHANGE_NAME = "logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
      /**
       * 声明一个 exchange
       * 1.exchange 的名称
       * 2.exchange 的类型
       */
      channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
      Scanner sc = new Scanner(System.in);
      System.out.println("请输入信息");
      while (sc.hasNext()) {
        String message = sc.nextLine();
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
        System.out.println("生产者发出消息" + message);
      }
  }
}

package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogs01 {
  private static final String EXCHANGE_NAME = "logs";

  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME,  BuiltinExchangeType.FANOUT);
    /**
     * 生成一个临时的队列 队列的名称是随机的
     * 当消费者断开和该队列的连接时 队列自动删除
     */
    String queueName = channel.queueDeclare().getQueue();
    //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
    channel.queueBind(queueName, EXCHANGE_NAME, "");
    System.out.println("等待接收消息,把接收到的消息打印在屏幕.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      System.out.println("控制台打印接收到的消息"+message);
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
  }
}
package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import org.apache.commons.io.FileUtils;

import java.io.File;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogs02 {
  private static final String EXCHANGE_NAME = "logs";

  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
    /**
     * 生成一个临时的队列 队列的名称是随机的
     * 当消费者断开和该队列的连接时 队列自动删除
     */
    String queueName = channel.queueDeclare().getQueue();
    //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
    channel.queueBind(queueName, EXCHANGE_NAME, "");
    System.out.println("等待接收消息,把接收到的消息写到文件.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      File file = new File("C:\\work\\rabbitmq_info.txt");
      FileUtils.writeStringToFile(file,message,"UTF-8");
      System.out.println("数据写入文件成功");
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
  }
}
运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
两个消费者都接收到了信息,这就是广播类型

Direct

 根据routingKey 来绑定交换机和队列
代码
package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import org.apache.commons.io.FileUtils;

import java.io.File;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogsDirect01 {

  private static final String EXCHANGE_NAME = "direct_logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
    String queueName = "disk";
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, EXCHANGE_NAME, "error");
    System.out.println("等待接收消息.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      message="接收绑定键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message;
      File file = new File("C:\\work\\rabbitmq_info.txt");
      FileUtils.writeStringToFile(file,message,"UTF-8");
      System.out.println("错误日志已经接收");
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
    });
  }
}

package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogsDirect02 {
  private static final String EXCHANGE_NAME = "direct_logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
    String queueName = "console";
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, EXCHANGE_NAME, "info");
    channel.queueBind(queueName, EXCHANGE_NAME, "warning");
    System.out.println("等待接收消息.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      System.out.println(" 接收绑定键 :"+delivery.getEnvelope().getRoutingKey()+", 消 息:"+message);
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
    });
  }
}

package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class EmitLogDirect {

  private static final String EXCHANGE_NAME = "direct_logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
      channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
      //创建多个 bindingKey
      Map<String, String> bindingKeyMap = new HashMap<>();
      bindingKeyMap.put("info","普通 info 信息");
      bindingKeyMap.put("warning","警告 warning 信息");
      bindingKeyMap.put("error","错误 error 信息");
      //debug 没有消费这接收这个消息 所有就丢失了
      bindingKeyMap.put("debug","调试 debug 信息");
      for (Map.Entry<String, String> bindingKeyEntry: bindingKeyMap.entrySet()){
        String bindingKey = bindingKeyEntry.getKey();
        String message = bindingKeyEntry.getValue();
        channel.basicPublish(EXCHANGE_NAME,bindingKey, null,
            message.getBytes("UTF-8"));
        System.out.println("生产者发出消息:" + message);
      }
  }

}
运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Topics

	 发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单
	词列表,以点号分隔开。这些单词可以是任意单词,比如说:"stock.usd.nyse", "nyse.vmw", 
	"quick.orange.rabbit".这种类型的。当然这个单词列表最多不能超过 255 个字节。
	在这个规则列表中,其中有两个替换符是大家需要注意的
	*(星号)可以代替一个单词
	#(井号)可以替代零个或多个单词
代码
package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogsTopic01 {
  private static final String EXCHANGE_NAME = "topic_logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME,  BuiltinExchangeType.TOPIC);
    //声明 Q1 队列与绑定关系
    String queueName="Q1";
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.*");
    System.out.println("等待接收消息.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      System.out.println(" 接收队列 :"+queueName+" 绑 定 键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
    });
  }
}

package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class ReceiveLogsTopic02 {
  private static final String EXCHANGE_NAME = "topic_logs";
  public static void main(String[] argv) throws Exception {
    Channel channel = MqConnectionUtil.getChannel();
    channel.exchangeDeclare(EXCHANGE_NAME,  BuiltinExchangeType.TOPIC);
    //声明 Q2 队列与绑定关系
    String queueName="Q2";
    channel.queueDeclare(queueName, false, false, false, null);
    channel.queueBind(queueName, EXCHANGE_NAME, "*.*.rabbit");
    channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");
    System.out.println("等待接收消息.....");
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      System.out.println(" 接收队列 :"+queueName+" 绑 定 键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
    });
  }
}

package com.leava.cloud.receive;

import com.leava.cloud.util.MqConnectionUtil;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description
 * @Author mq
 * @Version V1.0.0
 * @Date 2021/6/18 0018
 */
public class EmitLogTopic {
  private static final String EXCHANGE_NAME = "topic_logs";
  public static void main(String[] argv) throws Exception {
     Channel channel = MqConnectionUtil.getChannel()  ;
      channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
      /**
       * Q1-->绑定的是
       * 中间带 orange 带 3 个单词的字符串(*.orange.*)
       * Q2-->绑定的是
       * 最后一个单词是 rabbit 的 3 个单词(*.*.rabbit)
       * 第一个单词是 lazy 的多个单词(lazy.#)
       *
       */
      Map<String, String> bindingKeyMap = new HashMap<>();
      bindingKeyMap.put("quick.orange.rabbit","被队列 Q1Q2 接收到");
      bindingKeyMap.put("lazy.orange.elephant","被队列 Q1Q2 接收到");
      bindingKeyMap.put("quick.orange.fox","被队列 Q1 接收到");
      bindingKeyMap.put("lazy.brown.fox","被队列 Q2 接收到");
      bindingKeyMap.put("lazy.pink.rabbit","虽然满足两个绑定但只被队列 Q2 接收一次");
      bindingKeyMap.put("quick.brown.fox","不匹配任何绑定不会被任何队列接收到会被丢弃");
      bindingKeyMap.put("quick.orange.male.rabbit","是四个单词不匹配任何绑定会被丢弃");
      bindingKeyMap.put("lazy.orange.male.rabbit","是四个单词但匹配 Q2");
      for (Map.Entry<String, String> bindingKeyEntry: bindingKeyMap.entrySet()){
        String bindingKey = bindingKeyEntry.getKey();
        String message = bindingKeyEntry.getValue();
        channel.basicPublish(EXCHANGE_NAME,bindingKey, null,
            message.getBytes("UTF-8"));
        System.out.println("生产者发出消息" + message);
      }
    }
}

运行结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挚爱妲己~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值