【夏目鬼鬼分享】StringBoot整合RabbitMQ,使用Direct、Fanout、Topic三种模式

本文介绍了RabbitMQ的基本概念及其作为AMQP实现的功能特点,详细解释了fanout、direct和topic三种消息路由模式的工作原理,并通过实例演示了如何在Spring Boot项目中使用这些模式。
摘要由CSDN通过智能技术生成

RabbitMQ简介

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

下图展示了RabbitMQ在三种模式下的流程图

fanout

fanout类型的Exchange路由规则非常简单,它会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。

 

上图中,生产者(P)发送到Exchange(X)的所有消息都会路由到图中的两个Queue,并最终被两个消费者(C1与C2)消费。

direct

direct类型的Exchange路由规则也很简单,它会把消息路由到那些binding key与routing key完全匹配的Queue中。
 

 

以上图的配置为例,我们以routingKey=”error”发送消息到Exchange,则消息会路由到Queue1(amqp.gen-S9b…,这是由RabbitMQ自动生成的Queue名称)和Queue2(amqp.gen-Agl…);如果我们以routingKey=”info”或routingKey=”warning”来发送消息,则消息只会路由到Queue2。如果我们以其他routingKey发送消息,则消息不会路由到这两个Queue中。

topic

前面讲到direct类型的Exchange路由规则是完全匹配binding key与routing key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,但这里的匹配规则有些不同,它约定:

  • routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
  • binding key与routing key一样也是句点号“. ”分隔的字符串
  • binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

 

下面用代码来具体说明RabbitMQ的用法

 1先在服务器中安装RabbitMQ后,输入http://ip:15672/ 登陆,创建用户和虚拟主机,用户zhaobl ,虚拟主机为test

2创建两个springboot项目,springboot项目1(生产者),springboot项目2(消费者)。

pom.xml引用jar依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
  <version>2.1.7.RELEASE</version>
</dependency>

添加application.properties配置

spring.rabbitmq.host=xx.xx.xx.xx
spring.rabbitmq.port=5672
spring.rabbitmq.username=zhaobl
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=test

fanout模式

消费者和生产者项目创建配置类FanoutRabbitConfig.java

@Configuration
public class FanoutRabbitConfig {

    /**
     *  创建三个队列 :fanout.A   fanout.B  fanout.C
     *  将三个队列都绑定在交换机 fanoutExchange 上
     *  因为是扇型交换机, 路由键无需配置,配置也不起作用
     */


    @Bean
    public Queue queueA() {
        return new Queue("fanout.A");
    }

    @Bean
    public Queue queueB() {
        return new Queue("fanout.B");
    }

    @Bean
    public Queue queueC() {
        return new Queue("fanout.C");
    }

    @Bean
    FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }

    @Bean
    Binding bindingExchangeA() {
        return BindingBuilder.bind(queueA()).to(fanoutExchange());
    }

    @Bean
    Binding bindingExchangeB() {
        return BindingBuilder.bind(queueB()).to(fanoutExchange());
    }

    @Bean
    Binding bindingExchangeC() {
        return BindingBuilder.bind(queueC()).to(fanoutExchange());
    }
}

 生产者项目创建请求类SendMessageController.java

@RestController
public class SendMessageController {

    @Autowired
    RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,这提供了接收/发送等等方法


    @GetMapping("/sendFanoutMessage")
    public String sendFanoutMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "message: testFanoutMessage ";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        rabbitTemplate.convertAndSend("fanoutExchange", null, map);
        return "ok";
    }
}

 消费者项目创建接收消息类FanoutReceiverA.java、FanoutReceiverB.java、FanoutReceiverC.java

@Component
@RabbitListener(queues = "fanout.A")
public class FanoutReceiverA {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("FanoutReceiverA消费者收到消息  : " +testMessage.toString());
    }

}
@Component
@RabbitListener(queues = "fanout.B")
public class FanoutReceiverB {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("FanoutReceiverB消费者收到消息  : " +testMessage.toString());
    }

}
@Component
@RabbitListener(queues = "fanout.C")
public class FanoutReceiverC {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("FanoutReceiverC消费者收到消息  : " +testMessage.toString());
    }

}

 分别启动消费者和生产者项目,发送请求后消费者控制台显示消息

direct模式

消费者和生产者项目创建配置类DirectRabbitConfig.java

@Configuration
public class DirectRabbitConfig {

    //队列 起名:DirectRabbitQueue
    @Bean
    public Queue DirectRabbitQueue() {
        // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        // exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
        //   return new Queue("DirectRabbitQueue",true,true,false);

        //一般设置一下队列的持久化就好,其余两个就是默认false
        return new Queue("DirectRabbitQueue",true);
    }

    //Direct交换机 起名:DirectRabbitExchange
    @Bean
    DirectExchange DirectRabbitExchange() {
        //  return new DirectExchange("TestDirectExchange",true,true);
        return new DirectExchange("DirectRabbitExchange",true,false);
    }

    //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting
    @Bean
    Binding bindingDirect() {
        return BindingBuilder.bind(DirectRabbitQueue()).to(DirectRabbitExchange()).with("TestDirectRouting");
    }

}

 生产者项目创建请求类SendMessageController.java

@RestController
public class SendMessageController {

    @Autowired
    RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,这提供了接收/发送等等方法

    @GetMapping("/sendDirectMessage")
    public String sendDirectMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String,Object> map=new HashMap<>();
        map.put("messageId",messageId);
        map.put("messageData",messageData);
        map.put("createTime",createTime);
        //将消息携带绑定键值:DirectRabbitRouting 发送到交换机DirectRabbitExchange
        rabbitTemplate.convertAndSend("DirectRabbitExchange", "DirectRabbitRouting", map);
        return "ok";
    }

}

消费者项目创建接收消息类DirectReceiver1.java、DirectReceiver2.java

@Component
@RabbitListener(queues = "DirectRabbitQueue")//监听的队列名称 DirectRabbitQueue
public class DirectReceiver1 {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("第一个DirectReceiver消费者收到消息  : " + testMessage.toString());
    }

}
@Component
@RabbitListener(queues = "DirectRabbitQueue")//监听的队列名称 DirectRabbitQueue
public class DirectReceiver2 {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("第二个DirectReceiver消费者收到消息  : " + testMessage.toString());
    }

}

 分别启动消费者和生产者项目,发送4次请求后消费者控制台显示消息,可以看到消息是轮询发送的。

 topic模式

消费者和生产者项目创建配置类TopicRabbitConfig.java

@Configuration
public class TopicRabbitConfig {
    //绑定键
    public final static String man = "topic.man";
    public final static String woman = "topic.woman";

    @Bean
    public Queue firstQueue() {
        return new Queue(TopicRabbitConfig.man);
    }

    @Bean
    public Queue secondQueue() {
        return new Queue(TopicRabbitConfig.woman);
    }

    @Bean
    TopicExchange exchange() {
        return new TopicExchange("topicExchange");
    }


    //将firstQueue和topicExchange绑定,而且绑定的键值为topic.man
    //这样只要是消息携带的路由键是topic.man,才会分发到该队列
    @Bean
    Binding bindingExchangeMessage() {
        return BindingBuilder.bind(firstQueue()).to(exchange()).with(man);
    }

    //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#
    // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列
    @Bean
    Binding bindingExchangeMessage2() {
        return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");
    }

}

生产者项目创建请求类SendMessageController.java

@RestController
public class SendMessageController {

    @Autowired
    RabbitTemplate rabbitTemplate;  //使用RabbitTemplate,这提供了接收/发送等等方法

    @GetMapping("/sendTopicMessage1")
    public String sendTopicMessage1() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "message: M A N ";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> manMap = new HashMap<>();
        manMap.put("messageId", messageId);
        manMap.put("messageData", messageData);
        manMap.put("createTime", createTime);
        rabbitTemplate.convertAndSend("topicExchange", "topic.man", manMap);
        return "ok";
    }

    @GetMapping("/sendTopicMessage2")
    public String sendTopicMessage2() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "message: woman is all ";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> womanMap = new HashMap<>();
        womanMap.put("messageId", messageId);
        womanMap.put("messageData", messageData);
        womanMap.put("createTime", createTime);
        rabbitTemplate.convertAndSend("topicExchange", "topic.abc", womanMap);
        return "ok";
    }
}
  

消费者项目创建接收消息类TopicManReceiver.java、TopicTotalReceiver.java

@Component
@RabbitListener(queues = "topic.man")
public class TopicManReceiver {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("TopicManReceiver消费者收到消息  : " + testMessage.toString());
    }
}
@Component
@RabbitListener(queues = "topic.woman")
public class TopicTotalReceiver {

    @RabbitHandler
    public void process(Map testMessage) {
        System.out.println("TopicTotalReceiver消费者收到消息  : " + testMessage.toString());
    }
}

  分别启动消费者和生产者项目,发送sendTopicMessage1请求后消费者控制台显示消息,TopicManReceiver 和 TopicTotalReceiver 都接收到消息

 TopicManReceiver指定为topic.man能接收到topic.man的消息,TopicTotalReceiver指定为topic.#可以匹配到topic.man的消息

 发送sendTopicMessage2请求后只有TopicTotalReceiver接收到消息

 TopicManReceiver指定为topic.man不能接收到topic.abc的消息,TopicTotalReceiver指定为topic.#可以匹配到topic.abc的消息

到这里三种模式已经完毕了。

好的,以下是一个使用poi导出excel的案例,其中包含三个sheet,每个sheet的数据来自不同的对象集合。 ```java import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class ExcelExportExample { public static void main(String[] args) throws IOException { // 创建一个Workbook Workbook workbook = new XSSFWorkbook(); // 创建第一个Sheet,名为"Sheet1" Sheet sheet1 = workbook.createSheet("Sheet1"); // 创建第二个Sheet,名为"Sheet2" Sheet sheet2 = workbook.createSheet("Sheet2"); // 创建第三个Sheet,名为"Sheet3" Sheet sheet3 = workbook.createSheet("Sheet3"); // 准备Sheet1的数据 List<Sheet1Data> sheet1DataList = new ArrayList<>(); sheet1DataList.add(new Sheet1Data("张三", 20, "男")); sheet1DataList.add(new Sheet1Data("李四", 25, "女")); sheet1DataList.add(new Sheet1Data("王五", 30, "男")); // 写入Sheet1的数据 int rowIndex = 0; for (Sheet1Data data : sheet1DataList) { Row row = sheet1.createRow(rowIndex++); Cell cell0 = row.createCell(0); cell0.setCellValue(data.getName()); Cell cell1 = row.createCell(1); cell1.setCellValue(data.getAge()); Cell cell2 = row.createCell(2); cell2.setCellValue(data.getGender()); } // 准备Sheet2的数据 List<Sheet2Data> sheet2DataList = new ArrayList<>(); sheet2DataList.add(new Sheet2Data("A", 80)); sheet2DataList.add(new Sheet2Data("B", 75)); sheet2DataList.add(new Sheet2Data("C", 90)); // 写入Sheet2的数据 rowIndex = 0; for (Sheet2Data data : sheet2DataList) { Row row = sheet2.createRow(rowIndex++); Cell cell0 = row.createCell(0); cell0.setCellValue(data.getName()); Cell cell1 = row.createCell(1); cell1.setCellValue(data.getScore()); } // 准备Sheet3的数据 List<Sheet3Data> sheet3DataList = new ArrayList<>(); sheet3DataList.add(new Sheet3Data(1, "A", "2021-01-01")); sheet3DataList.add(new Sheet3Data(2, "B", "2021-01-02")); sheet3DataList.add(new Sheet3Data(3, "C", "2021-01-03")); // 写入Sheet3的数据 rowIndex = 0; for (Sheet3Data data : sheet3DataList) { Row row = sheet3.createRow(rowIndex++); Cell cell0 = row.createCell(0); cell0.setCellValue(data.getId()); Cell cell1 = row.createCell(1); cell1.setCellValue(data.getName()); Cell cell2 = row.createCell(2); cell2.setCellValue(data.getDate()); } // 将Workbook写入文件 FileOutputStream outputStream = new FileOutputStream("example.xlsx"); workbook.write(outputStream); outputStream.close(); System.out.println("Excel文件已成功导出。"); } // Sheet1的数据对象 static class Sheet1Data { private String name; private int age; private String gender; public Sheet1Data(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } public String getName() { return name; } public int getAge() { return age; } public String getGender() { return gender; } } // Sheet2的数据对象 static class Sheet2Data { private String name; private int score; public Sheet2Data(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } } // Sheet3的数据对象 static class Sheet3Data { private int id; private String name; private String date; public Sheet3Data(int id, String name, String date) { this.id = id; this.name = name; this.date = date; } public int getId() { return id; } public String getName() { return name; } public String getDate() { return date; } } } ``` 这个例子中,我们创建了一个Workbook,并创建了三个Sheet,分别为"Sheet1"、"Sheet2"、"Sheet3"。我们准备了三个不同的对象集合,分别为Sheet1Data、Sheet2Data、Sheet3Data,并将它们写入到Workbook的相应Sheet中。最后,将Workbook写入到文件中。 在这个例子中,我们使用了XSSFWorkbook类来创建Workbook,并使用了createSheet()方法来创建Sheet。我们通过创建Row对象和Cell对象来写入数据,然后通过FileOutputStream将Workbook写入到文件中。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值