RabbitMQ->Topic

RabbitMQ->Topic

 

在看了 上一个实验 RabbitMQ->路由订阅模型(direct) 之后,发现分级消息通知很适合我们的生活中的实际场景。

实验代码:CODE CHINA

 


目录

RabbitMQ->Topic

反思

实验

设计

代码

Step 1 创建常量类 Constant.java

Step2 创建 工具类 ConnectionUtil.java

Step3 创建 生产者 Producer

Step4 创建 消费者 Consumer

Step 5 运行程序


反思

反思上一个实验:

但是我们仔细思考一下,一个班级有很多个科任老师,每个科任老师都有自己的课程内容。

如果按照上面一个实验的流程来写,

        // EXCHANGE_NAME (班主任)
        // 绑定 info 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_INFO);
        // 绑定 warning 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_WARNING);
        // 绑定 error 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_ERROR);

        // EXCHANGE_NAME (数学老师)
        // 绑定 info 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_INFO);
        // 绑定 warning 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_WARNING);
        // 绑定 error 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_ERROR);

        // EXCHANGE_NAME (英语老师)
        // 绑定 info 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_INFO);
        // 绑定 warning 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_WARNING);
        // 绑定 error 键值
        channel.queueBind(queueName,Constant.EXCHANGE_NAME,Constant.ROUTING_KEY_ERROR);

        // ...

可以发现,代码会随着老师(交换机)的增多,代码的冗余度有点惨不忍睹

当然,我们能考虑到的,RabbitMQ的工作人员早就想到了,因此推出了 TOPIC 模型,

在 TOPIC 模型中,可以使用通配符进行绑定: * 匹配全部,当然前提是交换机的类型 为 topic 类型

 

在 topic 模式中有两个通配符:

* (start) can substitute for exactly one word.  匹配一个单词

# (hash) can substitute for zero or more words. 匹配一个或多个单词

 

在 topic 模式中,RoutingKey 一般是由多个单词组成的

 

以上图为例,

*.dcpnet.*  能匹配到三个队列

cn.# 能匹配到第一个和第三个队列

 

实验

设计

我们设定9个角色:三个消息生产者(校长、班主任、语文老师)、7个+消息消费者(一班班长、一班语文课代表、一班全体学生,二班语文课代表、二班学生、体育老师、其他老师)

分析:

班主任只带一个班:管理一班学生,有一个班长。通知一班学生放学,班长整队

语文老师带两个班的语文:管理两个班的语文,和两个语文课代表。通知两个班学生语文作业,通知语文课代表明天作业收到办公室

校长:管理全校学生和体育老师,通知全校学生进行课间操,体育老师组织

这个实验只是简单模拟学校的部分事务。

      语文老师布置作业:student.# 发送消息-> 作业内容

                     通知课代表收作业: manager.*.chinese. 发送消息-> 要收的作业

       班主任通知放学 : student.1.*  发送消息 -> 放学了,到门口排队

                   通知班长:manager.1.monitor  发送消息 -> 整队带出去

        校长通知课间操:# 全校师生课间操

                       体育老师: teacher.sports.* 组织课间操

 

开启项目

代码

添加依赖

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

Step 1 创建常量类 Constant.java

其中包含三个交换机的名字、路由键值、消息内容

    // 交换机名字:语、数、外老师
    /** 语文老师 */
    public static final String EXCHANGE_CHINESE_TEACHER = "ex_chinese";
    /** 班主任 */
    public static final String EXCHANGE_HEAD_TEACHER = "ex_head";
    /** 校长 */
    public static final String EXCHANGE_HEAD_MASTER= "ex_headmaster";


    // 消息内容
    /** 语文老师给所有同学的消息  : student.*.all */
    public static final String MESSAGE_TO_STUDENT_ALL = ">语文老师: 语文作业内容";
    /** 语文老师给语文课代表的消息 : student.*.chinese*/
    public static final String MESSAGE_TO_STUDENT_CHINESE = ">语文老师: 明天把语文作业收了,抱我办公室来";

    /** 班主任(1)给所有学生的消息 : student.1.* */
    public static final String MESSAGE_TO_STUDENT_ONE = ">班主任1: 放学了,到门口排队";
    /** 班主任给班长(1)的消息    : student.1.monitor */
    public static final String MESSAGE_TO_MONITOR = ">班主任1: 组织放学,整队";

    /** 校长给全校的消息 :  #.all */
    public static final String MESSAGE_TO_ALL = ">校长: 所有人到操场来课间操";
    /** 校长给体育老师的消息 : teacher.sports.* */
    public static final String MESSAGE_TO_TEACHER_SPORTS_ALL = ">校长: 体育老师带领课间操";

Step2 创建 工具类 ConnectionUtil.java

ConnectionUtil 中主要功能是 获取连接、释放资源

public class ConnectionUtil {

    /** Ip 地址 */
    private static final String IP_ADDRESS = "127.0.0.1";
    /** 固定端口 */
    private static final int PORT = 5672;

    // TODO 改成你自己的用户名
    /**  用户名 */
    private static final String USER_NAME = "root";

    // TODO 改成你自己的密码
    /** 密码 */
    private static final String PASSWORD = "dcpnet";

    /**
     * 获取连接
     * @return 连接
     * @throws IOException IOException
     * @throws TimeoutException TimeOutException
     */
    public static Connection getConnection() throws IOException, TimeoutException {
        /** 创建连接工厂 */
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 设置连接工厂属性
        // IP 地址
        connectionFactory.setHost(IP_ADDRESS);
        // 端口
        connectionFactory.setPort(PORT);
        // 用户名
        connectionFactory.setUsername(USER_NAME);
        // 密码
        connectionFactory.setPassword(PASSWORD);
        // 创建新的连接并且返回
        return connectionFactory.newConnection();
    }

    /**
     * 关闭资源
     * @param connection 连接
     * @param channel 信道
     */
    public static void close(Connection connection, Channel channel){
        try {
            channel.close();
            connection.close();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

Step3 创建 生产者 Producer

public class ProducerClient {
    public static void main(String[] args) throws Exception{
        // 获取连接、信道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // TODO 创建交换机:校长、班主任、语文老师
       /* channel.exchangeDeclare(Constant.EXCHANGE_HEAD_MASTER,"topic",true);
        channel.exchangeDeclare(Constant.EXCHANGE_HEAD_TEACHER,"topic",true);
        channel.exchangeDeclare(Constant.EXCHANGE_CHINESE_TEACHER,"topic",true);*/

        // 校长通知
        // 所有人做课间操
        channel.basicPublish(Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.all",null,Constant.MESSAGE_TO_ALL.getBytes());
        // 体育老师带操
        channel.basicPublish(Constant.EXCHANGE_HEAD_MASTER,"dcpnet.teacher.sports.all",null,Constant.MESSAGE_TO_TEACHER_SPORTS_ALL.getBytes());

        // 语文老师通知
        channel.basicPublish(Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.student.all.all",null,Constant.MESSAGE_TO_STUDENT_ALL.getBytes());
        channel.basicPublish(Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.manager.all.chinese",null,Constant.MESSAGE_TO_STUDENT_CHINESE.getBytes());

        // 班主任
        channel.basicPublish(Constant.EXCHANGE_HEAD_TEACHER,"dcpnet.student.1.all",null,Constant.MESSAGE_TO_STUDENT_ONE.getBytes());
        channel.basicPublish(Constant.EXCHANGE_HEAD_TEACHER,"dcpnet.manager.1.monitor",null,Constant.MESSAGE_TO_MONITOR.getBytes());

        // 释放资源
        ConnectionUtil.close(connection,channel);
    }
}

Step4 创建 消费者 Consumer

StudentC1N1

接受校长给所有人的全部消息

接受一班班主任给所有人的消息

接受一般班主任给班长的消息

接受语文老师给所有人的消息

public class StudentC1N1 {
    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定到 Exchange 班主任、语文老师、校长 的 student.1.1
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_TEACHER,"dcpnet.student.1.*");
        channel.queueBind(queueName,Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.student.#");
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.#");

        // 绑定到 Exchange 班主任 班长
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_TEACHER,"dcpnet.manager.1.monitor");

        channel.basicConsume(queueName,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[一班学生1-班长] 接收到消息 : " + new String(body));
            }
        });
    }
}

StudentC1N2

接受校长给所有人的全部消息

接受一班班主任给所有人的消息

接受语文老师给所有人的消息

接受语文老师给语文课代表的消息

public class StudentC1N2 {

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定到 Exchange 班主任、语文老师、校长 的 student.1.2
        channel.queueBind(queueName, Constant.EXCHANGE_HEAD_TEACHER, "dcpnet.student.1.*");
        channel.queueBind(queueName, Constant.EXCHANGE_CHINESE_TEACHER, "dcpnet.student.#");
        channel.queueBind(queueName, Constant.EXCHANGE_HEAD_MASTER, "dcpnet.all.#");

        // 绑定到 Exchange 语文老师 的 语文课代表
        channel.queueBind(queueName, Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.manager.*.chinese");

        channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[一班学生2-语文课代表] 接收到消息 : " + new String(body));
            }
        });
    }

}

StudentC1Nn

接受校长给所有人的全部消息

接受一班班主任给所有人的消息

接受语文老师给所有人的消息

public class StudentC1Nn {
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定到 Exchange 班主任、语文老师、校长 的 student.1.2
        channel.queueBind(queueName, Constant.EXCHANGE_HEAD_TEACHER, "dcpnet.student.1.*");
        channel.queueBind(queueName, Constant.EXCHANGE_CHINESE_TEACHER, "dcpnet.student.#");
        channel.queueBind(queueName, Constant.EXCHANGE_HEAD_MASTER, "dcpnet.all.#");

        channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[一班学生n] 接收到消息 : " + new String(body));
            }
        });
    }
}

StudentC2N1

接受校长给所有人的全部消息

接受语文老师给所有人的消息

接受语文老师给课代表的消息

public class StudentC2N1 {

    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定到 Exchange 班主任、语文老师、校长 的 student.2.1
        channel.queueBind(queueName,Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.student.#");
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.#");

        // 绑定到 Exchange 班主任 班长
        channel.queueBind(queueName,Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.manager.*.chinese");

        channel.basicConsume(queueName,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[二班学生1-语文课代表] 接收到消息 : " + new String(body));
            }
        });
    }

}

StudentC2Nn

接受校长给所有人的全部消息

接受语文老师给所有人的消息

public class StudentC2Nn {

    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定到 Exchange 语文老师 的 student.2.n
        channel.queueBind(queueName,Constant.EXCHANGE_CHINESE_TEACHER,"dcpnet.student.#");
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.#");


        channel.basicConsume(queueName,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[二班学生n] 接收到消息 : " + new String(body));
            }
        });
    }
}

TeacherSportN1

接受校长给所有人的全部消息

接受校长给体育老师的消息

public class TeacherSportsN1 {
    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 所有教师的消息
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.#");

        // 体育教师的消息
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.teacher.sports.*");

        channel.basicConsume(queueName,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[体育老师1] 接收到消息 : " + new String(body));
            }
        });
    }
}

TeacherOtherNn

接受校长给所有人的全部消息

public class TeacherOtherNn {
    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 获取一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 所有教师
        channel.queueBind(queueName,Constant.EXCHANGE_HEAD_MASTER,"dcpnet.all.#");

        channel.basicConsume(queueName,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[Other老师n] 接收到消息 : " + new String(body));
            }
        });
    }
}

 

Step 5 运行程序

首先确保有三个交换机 

    // 交换机名字:语、数、外老师
    /** 语文老师 */
    public static final String EXCHANGE_CHINESE_TEACHER = "ex_chinese";
    /** 班主任 */
    public static final String EXCHANGE_HEAD_TEACHER = "ex_head";
    /** 校长 */
    public static final String EXCHANGE_HEAD_MASTER= "ex_headmaster";

如果没有这三个 topic 类型的交换机,则先进行创建

方式1:

将生产者中 TODO 注释后面的几行代码取消注释,第一次执行的时候运行

方式二:

在web 管理页面添加

正式开始运行:

1,首先运行所有的消费者

2.运行生产者发送消息

可以看到,

学生1-班长 接收到了校长给所有人的消息、班主任给所有人的消息、语文老师给所有人的消息、班主任给班长的消息

学生2-语文课代表 接收到了校长给所有人的消息、班主任给所有人的消息、语文老师给所有人的消息、语文老师给语文课代表的消息

学生n 接收到了校长给所有人的消息、班主任给所有人的消息、语文老师给所有人的消息

学生2-语文课代表 接收到了校长给所有人的消息、语文老师给所有人的消息、语文老师给语文课代表的消息

学生2-n 接收到了校长给所有人的消息、语文老师给所有人的消息

体育教师1 接收到了校长给所有热的消息、校长给体育老师的消息

其他老师n 接收到了校长给所有的老师的消息

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值