RabbitMQ之Exchange类型详解

1.来介绍一下Exchange的类型

常用的Exchange类型有4种

  • direct:如其名,Exchange将消息发送对应的Queue(RoutingKey=BindingKey)
  • topic:Exchange将消息发送给匹配RoutingKey的BindingKey所绑定Queue(RoutingKey符合BindingKey)
  • fanout:Exchange将消息发送给该Exchange绑定的所有Queue(RoutingKey和BindingKey没存在的必要,可以随便写)
  • headers:通过配置一些key-value将Exchange与Queue进行绑定(RoutingKey和BindingKey没必要存在,可以随便写)

可能大家不是很理解,下面来看代码就知道了!
引入依赖

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.6.0</version>
</dependency>

1.1Direct

下面这个类用于创建一个与RabbitMQ的Connection(连接),该Connection用于创建Channel(信道),Channel是消息读写的通道,也就是我们的操作都会在Channel的基础之上进行

package com.dfyang.rabbitmq;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class RabbitmqConnectionFactory {
    private static ConnectionFactory factory = new ConnectionFactory();

    private static String HOST = "192.168.195.123";
    private static int PORT = 5672;
    private static String USERNAME = "root";
    private static String PASSWORD = "151310";

    static {
        factory.setHost(HOST);
        factory.setPort(PORT);
        factory.setUsername(USERNAME);
        factory.setPassword(PASSWORD);
    }

    public static Connection newConnection() {
        try {
            return factory.newConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.dfyang.rabbitmq.pc0;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Producer {

    private static String EXCHANGE_NAME = "exchange0";
    private static String QUEUE_NAME = "queue0";
    private static String BINDING_KEY = "key0";
    private static String ROUTING_KEY = "key0";
    private static String MESSAGE = "pc0发送消息";

    public static void main(String[] args) throws Exception {
        //创建一个Connection连接
        Connection connection = RabbitmqConnectionFactory.newConnection();
        //开启Channel
        Channel channel = connection.createChannel();
        //创建一个Exchange,设置名称和类型为direct
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        //创建一个Queue,设置名称,后面先不管
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //建立Queue与Exchange之间的BindingKey
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, BINDING_KEY);
        //使用生产者发布一条消息和RoutingKey
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,  null, MESSAGE.getBytes());
        //关闭Channel与Connection
        channel.close();
        connection.close();
    }
}

上面代码我们干了什么?

  • 创建与RabbitMQ的Connection,通过Connection创建一个后面用于读写的Channel
  • 通过Channel创建一个名称为exchange0的Exchange,其类型为direct
  • 通过Channel创建一个名称为queue0的Queue
  • 通过Channel建立exchange0与queue0的路由规则key0(BindingKey)
  • 通过发送消息和路由键key0到exchange0,exchange0将key0与BindingKey(key0)进行匹配,发现匹配成功,将消息发送到queue0

后面将不会对一些基础的进行详细的描述,请读懂上面这个再往下看
执行上述代码,进入可视化管理界面,发现RabbitMQ已经帮我们创建好了Exchange和Queue,并建立了绑定关系,而且我们可以看到queue0是有一个消息正在准备被消费的状态不会的点这
RabbitMQ之Exchange类型详解_第1张图片
在这里插入图片描述

——我们将ROUTING_KEY改为其他,会发现Queue0的Ready还是为1,因为我们的Exchange为direct类型,因此RoutingKey必须与BindingKey相等才能路由消息到Queue
RabbitMQ之Exchange类型详解_第2张图片
下面我们来将这个消息消费掉

package com.dfyang.rabbitmq.pc0;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Consumer {
    private static String QUEUE_NAME = "queue0";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitmqConnectionFactory.newConnection();
        Channel channel = connection.createChannel();
        //创建消费者,传入信道,因为消费者需要在信道的基础上消费数据
        //并重写获取消息的处理逻辑
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println("consumerTag(消费者标签,区分消费者):"
                        + consumerTag);
                System.out.println("envelope(封装了一些本次路由相关的信息):"
                        + envelope.toString());
                System.out.println("msg(消息体):" + new String(body));
            }
        };
        //Consumer消费消息,传入Queue
        //第二个参数为是否自动响应,设为true表示消费者接受消息表示这条消息已经消费了
        //否则即使获取到消息,该消息还会存在Queue中
        channel.basicConsume(QUEUE_NAME, true, consumer);
        //这里停留1s,防止执行慢了资源关闭
        TimeUnit.SECONDS.sleep(1);
        channel.close();
        connection.close();
    }
}

结果
这里对envelope进行一些解释

  • deliveryTag:消费者标签,区分多个消费者
  • redeliver:是否再次交付(当消费者获取消息,如果basicConsume第二个参数设置为false,也就是不进行交付,可以通过Producer再发送一个消息,用Consumer消费两次,发现第二次依旧能接受到消息,并且再第二次redeliver变为了true,也就是这是再次提交了)了解一下就行了!
  • 后面两个就不用说了
    在这里插入图片描述

1.2Topic

介绍Topic所使用的匹配符

  • *:匹配小数点前后一个
  • #:匹配小数点前后多个

eg.BindingKey:user . * (可以匹配user.delete,user.find,user.update)
eg.BindingKey:* . * . user (可以匹配delete.find.test)
eg.BindingKey:#. user (可以匹配delete.user,delete.find.user)
eg.BindingKey:user.# (可以匹配user.find.delete,user.find.find.find.find)
很好理解,希望大家能看懂
Producer

package com.dfyang.rabbitmq.pc1;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Producer {
    private static String EXCHANGE_NAME = "exchange1";
    private static String QUEUE_NAME = "queue1";
    private static String BINDING_KEY = "user.*";
    private static String ROUTING_KEY0 = "user.delete";
    private static String ROUTING_KEY1 = "user.update";
    private static String ROUTING_KEY2 = "user.delete.find";
    private static String MESSAGE0 = "user.delete";
    private static String MESSAGE1 = "user.update";
    private static String MESSAGE2 = "user.delete.find";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitmqConnectionFactory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, BINDING_KEY);
        //发送RoutingKey:user.delete
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY0,  null, MESSAGE0.getBytes());
        //发送RoutingKey:user.update
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY1,  null, MESSAGE1.getBytes());
        //发送RoutingKey:user.delete.find
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY2,  null, MESSAGE2.getBytes());
        channel.close();
        connection.close();
    }
}

这里创建了BindingKey为user.*,并发送了三个RoutingKey分别为user.delete、user.update、user.delete.find的消息,如果弄懂了上面.和#,那么这里很明显user.delete、user.update的消息将被路由到queue1,而user.delete.find的消息将被pass掉

Consumer

package com.dfyang.rabbitmq.pc1;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Consumer {
    private static String QUEUE_NAME = "queue1";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitmqConnectionFactory.newConnection();
        Channel channel = connection.createChannel();
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
                System.out.println(new String(body));
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);
        TimeUnit.SECONDS.sleep(1);
        channel.close();
        connection.close();
    }
}

结果如下,#这里就不测试了
在这里插入图片描述
RabbitMQ之Exchange类型详解_第3张图片

1.3fanout

Producer
这里先声明一下,由于fanout模式Exchange会自动将发送到该Exchange的消息路由到所有与该Exchange所绑定的Queue,所以路由键以及绑定键就没有必要存在了,但还是得要,因为不要会报错,所以这里名字可以随便写,反正没什么用处。注意理解,绑定键是进行路由的规则,路由键是匹配这个规则,而我们此时不需要规则。

package com.dfyang.rabbitmq.pc2;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Producer {

    private static String EXCHANGE_NAME = "exchange2";
    private static String QUEUE_NAME0 = "queue2.0";
    private static String QUEUE_NAME1 = "queue2.1";
    private static String MESSAGE = "发送fanout";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitmqConnectionFactory.newConnection();
        Channel channel = connection.createChannel();
        //生命一个Exchange,名称为exchange2
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        //声明两个Queue,queue2.0和queue2.1
        channel.queueDeclare(QUEUE_NAME0, true, false, false, null);
        channel.queueDeclare(QUEUE_NAME1, true, false, false, null);
        //将exchange2分别与两个queue进行绑定
        channel.queueBind(QUEUE_NAME0, EXCHANGE_NAME, "BindingKey");
        channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "BindingKey");
        channel.basicPublish(EXCHANGE_NAME, "RoutingKey",  null, MESSAGE.getBytes());
        channel.close();
        connection.close();
    }
}

很明显,消息将发送到queue2.0和queue2.1,执行上面的程序,查询可视化界面
在这里插入图片描述

1.4headers

下面这段代码是创建一个类型为headers得Exchange,和一个Queue,并将他们通过一个headers进行绑定,也就是必须匹配里面得参数才能成功路由

package com.dfyang.rabbitmq.pc3;

import com.dfyang.rabbitmq.RabbitmqConnectionFactory;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

public class Producer {
    private static String EXCHANGE_NAME = "exchange3";
    private static String QUEUE_NAME = "queue3";

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitmqConnectionFactory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "headers");
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        Map<String, Object> headers = new HashMap<>();
        headers.put("username", "root");
        headers.put("password", "151310");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "headers", headers);
        AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder().headers(headers);
        channel.close();
        connection.close();
    }
}

执行程序
在这里插入图片描述
RabbitMQ之Exchange类型详解_第4张图片
可以看到RabbitMQ已经为我们创建了exchange3,并且类型为headers,接下来我们使用可是可视化页面进行发布消息
先发送错误的消息
点击exchange3 -> 点击Publish message
提示我们消息发布了,但没有路由到的Queue,也就是没有匹配的
RabbitMQ之Exchange类型详解_第5张图片
接下来发送正确的消息,很明显发送成功,此时我们的queue中也多了一个消息,可以进入queue3查看消息的具体内容
RabbitMQ之Exchange类型详解_第6张图片

RabbitMQ之Exchange类型详解_第7张图片

这里大家应该明白了Exchange的类型

这里再解释一下BindingKey,我们可以看到再对Exchange和Queue进行绑定时,参数提示为routingKey而不是bindingKey,因为除了Exchange为topic类型外,其他类型我们可以认为BindingKey与RoutingKey是相等的,所有没必要太过于区分
RabbitMQ之Exchange类型详解_第8张图片

结束!

你可能感兴趣的:(RabbitMQ之Exchange类型详解)