RabbitMQ学习——常见概念详解

版权声明:欢迎转载,转载请指明出处 https://blog.csdn.net/yjw123456/article/details/84892035

Exchange

描述

用于接收消息,并根据路由键转发消息到所绑定的队列

结构图

在这里插入图片描述

蓝色框表示发送消息,然后消息通过路由关系路由到Queue1或Queue2

绿色框表示接收消息,消费者与队列建立监听,去消费消息

黄色框表示路由键绑定关系

交换机属性

  • Name:交换机名称
  • Type:交换机类型direct、topic、fanout、headers
  • Durablity:是否需要持久化 true|false
  • Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange
  • Internal:当前Exchange是否用于RabbitMQ内部使用,默认为false
  • Arguments:扩展参数,用于扩展AMQP协议自制定使用

Direct Exchange

  • 所有发送到Direct Exchange的消息被转发到RoutingKey中指定的Queue

在这里插入图片描述

如图所示,路由到队列名为Key的队列中去了

生产者代码:

public class Producer4DirectExchange {
    public static void main(String[] args) throws IOException {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();

        //声明
        String exchangeName = "test_direct_exchange";
        String routingKey = "test.direct";

        //发送
        String msg = "Hello, I am Producer4DirectExchange";
        channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());
    }
}

消费者代码:

    public static void main(String[] args) throws IOException, InterruptedException {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();
        //声明
        String exchangeName = "test_direct_exchange";
        String exchangeType = "direct";
        String queueName = "test_direct_queue";
        String routingKey = "test.direct";

        //声明Exchange
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);

        //声明一个队列
        channel.queueDeclare(queueName,false,false,false,null);

        //建立一个绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);

        //durable 是否持久化消息
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //参数:队列名称,是否自动ACK,Consumer
        channel.basicConsume(queueName,true,consumer);
        while (true) {
            //阻塞获取消息
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.out.println("收到消息:"+msg);
        }


    }

公共类:

public class ConnectionUtil {
    public static final String MQ_HOST = "192.168.222.101";
    public static final String MQ_VHOST = "/";
    public static final int MQ_PORT = 5672;

    public static Connection getConn() {
        //1. 创建一个ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(MQ_HOST);//配置host
        connectionFactory.setPort(MQ_PORT);//配置port
        connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost

        //2. 通过连接工厂创建连接
        try {
            return connectionFactory.newConnection();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

登录控制台,可以看到名为test_direct_exchange的交换机通过路由键test.direct绑定到test_direct_queue

在这里插入图片描述

Topic Exchange

  • 所有发送到Topic Exchange的消息都被转发到所有关心RoutingKey中指定Topic的队列上
  • Exchange将RoutingKey和某Topic进行模糊匹配,此时队列需要绑定一个Topic

可以使用通配符进行模糊匹配:
# 匹配一个或多个词(单词的意思,不是一个字符)
*匹配一个词
log.# 能匹配到"log.info.a"
log.* 只匹配"log.error"

在这里插入图片描述

如上图,比如,user.news和user.weather能路由到第一个队列

生成者代码:

public class Producer4TopicExchange {
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();

        //声明
        String exchangeName = "test_topic_exchange";
        String routingKey1 = "user.save";
        String routingKey2 = "user.update";
        String routingKey3 = "user.find.abc";


        //发送
        String msg = "Hello, I am Producer4TopicExchange";
        channel.basicPublish(exchangeName,routingKey1,null,msg.getBytes());
        channel.basicPublish(exchangeName,routingKey2,null,msg.getBytes());
        channel.basicPublish(exchangeName,routingKey3,null,msg.getBytes());

        CloseTool.closeElegantly(channel,connection);
    }
}

消费者代码:


public class Consumer4TopicExchange {
    public static void main(String[] args) throws IOException, InterruptedException {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();
        //声明
        String exchangeName = "test_topic_exchange";
        String exchangeType = "topic";
        String queueName = "test_topic_queue";
        String routingKey = "user.#";

        //声明Exchange
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);

        //声明一个队列
        channel.queueDeclare(queueName,false,false,false,null);

        //建立一个绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);

        //durable 是否持久化消息
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //参数:队列名称,是否自动ACK,Consumer
        channel.basicConsume(queueName,true,consumer);
        while (true) {
            //阻塞获取消息
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.out.println("收到消息:"+msg);
        }

    }
}

先启动消费者,然后发现下面两个绑定关系:

在这里插入图片描述

队列

在这里插入图片描述

再启动生产者,从消费者端可以看到如下输出:

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

说明3条消息都收到了,接下来,我们改一下消费者的路由键,改为:String routingKey = "user.*";

再次启动

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

奇怪,怎么还是收到3条,带着疑问我们去看控制台

在这里插入图片描述
可以看到,之前的路由规则绑定还在。因此可以解释为啥能收到3条。

点击unbind解绑"user.#"

然后继续操作一把,查看输出

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

此时,只收到两条了。

Fanout Exchange

  • 不处理路由键,只需要简单的将队列绑定到交换机上
  • 发送到交换机的消息都会被转发到与该交互机绑定的所有队列上
  • 转发消息是最快的

在这里插入图片描述

消息不走路由键,只要队列与交换机有绑定关系就能收到。

生产者:

public class Producer4FanoutExchange {
    public static void main(String[] args) throws IOException {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();

        //声明
        String exchangeName = "test_fanout_exchange";
        String routingKey = "nothing";

        //发送
        String msg = "Hello, I am Producer4FanoutExchange";
        channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());

        CloseTool.closeElegantly(channel,connection);
    }
}

消费者:


public class Consumer4FanoutExchange {
    public static void main(String[] args) throws IOException, InterruptedException {
        Connection connection = ConnectionUtil.getConn();

        Channel channel = connection.createChannel();
        //声明
        String exchangeName = "test_fanout_exchange";
        String exchangeType = "fanout";
        String queueName = "test_fanout_queue";
        String routingKey = "";//不设置路由键

        //声明Exchange
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);
        //声明一个队列
        channel.queueDeclare(queueName,false,false,false,null);
        //建立一个绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);

        //durable 是否持久化消息
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //参数:队列名称,是否自动ACK,Consumer
        channel.basicConsume(queueName,true,consumer);
        while (true) {
            //阻塞获取消息
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.out.println("收到消息:"+msg);
        }



    }
}

在这里插入图片描述

在这里插入图片描述

Binding

  • Exchange和Exchange、Queue之间的连接关系
  • Binding中可以包含RoutingKey或参数

Queue

  • 消息队列,实际存储消息

属性

  • Durability:是否持久化
  • Auto delete: 若为yes,代表当最后一个监听被移除后,该Queue会自动被删除

Message

  • 服务器和应用程序之间传递的数据
  • 本质就是一段数据,由Properties和Payload(Body)组成

属性

  • delivery mode
  • headers(自定义属性放到这里面)
  • content_type
  • content_encoding
  • priority
  • correlation_id
  • reply_to:指令消息失败返回的队列
  • expiration:过期时间
  • message_id:消息ID

Producer:


public class Producer {
    public static final String MQ_HOST = "192.168.222.101";
    public static final String MQ_VHOST = "/";
    public static final int MQ_PORT = 5672;

    public static void main(String[] args) throws IOException, TimeoutException {
        //1. 创建一个ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(MQ_HOST);//配置host
        connectionFactory.setPort(MQ_PORT);//配置port
        connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost

        //2. 通过连接工厂创建连接
        Connection connection = connectionFactory.newConnection();
        //3. 通过connection创建一个Channel
        Channel channel = connection.createChannel();

        Map<String,Object> headers = new HashMap<>();
        headers.put("var1","abc");
        headers.put("var2","sdd");
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2) //2:持久化投递;1:非持久化(未消费的消息重启后就没了)
                .contentEncoding("UTF-8")
                .expiration("5000")//5s
                .headers(headers)
                .build();

        //4. 通过Channel发送数据
        for (int i = 0; i < 10; i++) {
            String message = "Hello" + i;
            //exchange为"",则通过routingKey取寻找队列
            channel.basicPublish("","testQueue",properties,message.getBytes());
        }

        //5. 关闭连接
        channel.close();
        connection.close();

    }
}

consumer:

public class Consumer {


    public static final String QUEUE_NAME = "testQueue";

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConn();

        //3. 通过connection创建一个Channel
        Channel channel = connection.createChannel();

        //4. 声明(创建)一个队列
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        //5. 创建消费者
        QueueingConsumer queueingConsumer = new QueueingConsumer(channel);

        //6. 设置Channel
        channel.basicConsume(QUEUE_NAME,true,queueingConsumer);

        int num = 0;
        //7. 获取消息
        while (true) {
            //nextDelivery 会阻塞直到有消息过来
            QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("收到:" + message);
            Map<String,Object > headers = delivery.getProperties().getHeaders();
            System.out.println("headers get va1 :" + headers.get("var1"));
        }


    }
}

Virtual host - 虚拟主机

  • 虚拟地址,用于进行逻辑隔离,最上层的消息路由
  • 一个Virtuall Host里面可以有若干个Exchange和Queue
  • 同一个Virtual Host里面不能有相同名称的Exchange或Queue

猜你喜欢

转载自blog.csdn.net/yjw123456/article/details/84892035