SpringBoot整合消息中间件 RabbitMQ 第 7 篇 —— Topic 主题模式(通配符模式)

 

说明:此模式实在路由key模式的基础上,使用了通配符来管理消费者接收消息。生产者P发送消息到交换机X,type=topic,交换机根据绑定队列的routing key的值进行通配符匹配。

*(星号)可以代替一个单词,如:【info.*】只能匹配如 【info.email】或者【info.sms】

# (哈希)可以代替零个或多个单词。如:【info.#】可以匹配【info.email.user】或者【info.email.user.register】

OK,直接上代码。代码结构图:

bootstrap.yml 配置如下:

spring:
  rabbitmq:
    #主机名
    host: 127.0.0.1
    #端口号
    port: 5672
    #账号
    username: guest
    #密码
    password: guest
    #虚拟主机,这里配置的是我们的测试主机
    virtual-host: /test_host

# 自定义配置信息
queueConfig:
  # 邮件队列名
  emailQueue: topicEmailQueue
  # 短信队列名
  SMSQueue: topicSMSQueue
  # 交换机名称
  topicExchangeName: topicExchangeName
  # * 号路由,仅能匹配一个单词
  logStartRoute: log.*
  # # 号路由,能匹配0个或多个单词
  logHashRoute: log.#

server:
  port: 8080

# 将SpringBoot项目作为单实例部署调试时,不需要注册到注册中心
eureka:
  client:
    fetch-registry: false
    register-with-eureka: false

 

配置类:QueueConfig 代码如下:例子中,email 队列绑定 * 号和# 号的路由,其实可以只绑定 # 路由。

package com.study.config;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author biandan
 * @description
 * @signature 让天下没有难写的代码
 * @create 2021-04-05 上午 12:39
 */
@Configuration
public class QueueConfig {

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Value("${spring.rabbitmq.port}")
    private Integer port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;


    //邮件队列名
    @Value("${queueConfig.emailQueue}")
    private String emailQueue;

    //短信队列名
    @Value("${queueConfig.SMSQueue}")
    private String SMSQueue;

    //交换机名称
    @Value("${queueConfig.topicExchangeName}")
    private String topicExchangeName;

    // * 号路由,仅能匹配一个单词
    @Value("${queueConfig.logStartRoute}")
    private String logStartRoute;

    //# 号路由,能匹配0个或多个单词
    @Value("${queueConfig.logHashRoute}")
    private String logHashRoute;


    /**
     * 封装连接类
     *
     * @return
     */
    @Bean
    public CachingConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        return connectionFactory;
    }


    /**
     * 动态的创建队列(这里仅创建配置文件里的一个)
     *
     * @return
     * @throws Exception
     */
    @Bean
    public String getQueueName() throws Exception {
        //获取连接
        Connection connection = connectionFactory().createConnection();

        //创建通道。true表示有事务功能
        Channel channel = connection.createChannel(true);

         /*
        创建队列声明,参数说明:
        1.队列名queue

        2.是否持久化durable。是否持久化, 队列的声明默认是存放到内存中的,
        如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,
        保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库

        3.是否排外exclusive。有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的private,
        如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,
        如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常。
        一般等于true的话用于一个队列只能有一个消费者来消费的场景

        4.是否自动删除autoDelete。当最后一个消费者断开连接之后队列是否自动被删除,
        可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除

        5.其它参数 Map<String, Object> arguments
        */
        channel.queueDeclare(emailQueue, true, false, false, null);//创建邮件队列
        channel.queueDeclare(SMSQueue, true, false, false, null);//创建短信队列

        //创建交换机
        channel.exchangeDeclare(topicExchangeName, BuiltinExchangeType.TOPIC, true, false, null);

        //为短信队列绑定 * 号的路由
        channel.queueBind(SMSQueue, topicExchangeName, logStartRoute);

        //为邮件队列绑定 * 号和 # 号的路由(本例子中,#号路由已经涵盖了*号路由)
        channel.queueBind(emailQueue, topicExchangeName, logStartRoute);
        channel.queueBind(emailQueue, topicExchangeName, logHashRoute);

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

        return "";
    }
}

消息生产者:TopicProducer 代码如下。

package com.study.producer;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author biandan
 * @description 消息生产者(主题交换机)
 * @signature 让天下没有难写的代码
 * @create 2021-04-04 下午 10:49
 */
@Component
public class TopicProducer {

    private SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    //交换机名称
    @Value("${queueConfig.topicExchangeName}")
    private String topicExchangeName;

    // 定义 * 号路由,仅能匹配一个单词
    private String logStart = "log.register";

    // 定义 # 号路由,能匹配0个或多个单词
    private String logHash = "log.register.error";

    /**
     * 注入 AMQP 消息模板
     */
    @Autowired
    private AmqpTemplate template;

    /**
     * 每隔5秒产生一条消息
     */
    @Scheduled(fixedRate = 1000 * 5)
    public void sendMsg() {
        String registerMsg = "主题交换机消息生产者:用户注册 register:" + SDF.format(new Date());
        String errorMsg = "主题交换机消息生产者 用户注册错误信息 error:" + SDF.format(new Date());
        System.out.println(registerMsg);
        System.out.println(errorMsg);

        //发送消息
        template.convertAndSend(topicExchangeName, logStart, registerMsg);
        template.convertAndSend(topicExchangeName, logHash, errorMsg);

    }

}

 

启动类:

package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling //启用任务调度
@EnableEurekaClient
public class RabbitMQTopicApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitMQTopicApplication.class, args);
    }
}

消费者。

email

package com.study.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author biandan
 * @description 邮件消费者
 * @signature 让天下没有难写的代码
 * @create 2021-04-04 下午 11:39
 */
@Component
@RabbitListener(queues = "${queueConfig.emailQueue}")
public class Consumer_Email {

    @RabbitHandler
    public void receiveMsg(String msg) {
        System.out.println("邮件消费者_消费掉的消息:" + msg);
    }

}

 

短信:

package com.study.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author biandan
 * @description 短信消费者
 * @signature 让天下没有难写的代码
 * @create 2021-04-04 下午 11:39
 */
@Component
@RabbitListener(queues = "${queueConfig.SMSQueue}")
public class Consumer_SMS {

    @RabbitHandler
    public void receiveMsg(String msg) {
        System.out.println("短信消费者_消费掉的消息:" + msg);
    }

}

OK,启动项目,控制台输出如图:说明邮件队列、短信队列都收到了 * 号路由的信息,而只有邮件队列收到了 # 号路由的信息。(即 error 的信息只有邮件队列收到,因为只有邮件队列绑定了 # 号路由)

打开 RabbitMQ 管理后台,看到交换机绑定情况如图:

 

代码在百度网盘连接:https://pan.baidu.com/s/1Izb0WbsuLCJJXlm445xjrw   提取码:xnjd

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值