RabbitMQ入门(五)Exchanges(交换机)五种消息模式

前言:

RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。

相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面将它们推入队列。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们放到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。

Exchanges的类型

  • 直接(direct):处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 abc ,则只有被标记为 abc 的消息才被转发,不会转发 abc.def,也不会转发 dog.ghi,只会转发 abc。

  • 主题(topic):将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号 * 匹配不多不少一个词。因此 abc.# 能够匹配到 abc.def.ghi,但是 abc.* 只会匹配到 abc.def。

  • 标题(headers):不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定 Queue 与 Exchange 时指定一组键值对;当消息发送到RabbitMQ 时会取到该消息的 headers 与 Exchange 绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 属性是一个键值对,可以是 Hashtable,键值对的值可以是任何类型。而 fanout,direct,topic 的路由键都需要要字符串形式的。

    匹配规则 x-match 有下列两种类型:

    x-match = all :表示所有的键值对都匹配才能接受到消息

    x-match = any :表示只要有键值对匹配就能接受到消息

  • 扇出(fanout):不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout 交换机转发消息是最快的。

  • 实战:

    这里我集成了Swagger2,有兴趣的可以集成,嫌麻烦可以把controller上的注解配置删除。

1. 服务器安装好rabbitmq,这里不做过多说明

2. pom文件:

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-amqp
        

        
            org.springframework.boot
            spring-boot-starter-test
        
        
            io.springfox
            springfox-swagger2
            2.6.0
        
        
            io.springfox
            springfox-swagger-ui
            2.6.0
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    

3. producerController生产消息方

package com.fan.testsendupdatell.controller;

import io.swagger.annotations.ApiOperation;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;

@RestController
public class ProducerController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //helloWorld 直连模式
    @ApiOperation(value = "helloWorld发送接口", notes = "直接发送到队列")
    @GetMapping(value = "/helloWorldSend/{message}")
    public Object helloWorldSend(@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {
        //设置部分请求参数
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //发消息
        rabbitTemplate.send("helloWorldqueue", new Message(message.getBytes("UTF-8"), messageProperties));
        return "message sended : " + message;
    }

    //helloWorld 直连模式
    @ApiOperation(value = "helloWorld发送接口", notes = "直接发送到队列")
    @GetMapping(value = "/helloWorldSend2/{message}")
    public Object helloWorldSend2(@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {
        //设置部分请求参数
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //发消息
        ConcurrentHashMap myMap = new ConcurrentHashMap();
        myMap.put(0,"测试");
        myMap.put(1,"历史");
        myMap.put(2,"掌声");
        myMap.put(3,"周六");
        //convertAndSend与send都是发送消息,但是convertAndSend会自动序列化消息发送
        rabbitTemplate.convertAndSend("helloWorldqueue2", new Message(myMap.toString().getBytes(StandardCharsets.UTF_8), messageProperties));
        return "message sended : " + message;
    }

    //工作队列模式
    @ApiOperation(value = "workqueue发送接口", notes = "发送到所有监听该队列的消费")
    @GetMapping(value = "/workqueueSend/{message}")
    public Object workqueueSend(@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //制造多个消息进行发送操作
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.send("work_sb_mq_q", new Message(message.getBytes("UTF-8"), messageProperties));
        }
        return "message sended : " + message;
    }


    // pub/sub 发布订阅模式   交换机类型 fanout
    @ApiOperation(value = "fanout发送接口", notes = "发送到fanoutExchange。消息将往该exchange下的所有queue转发")
    @GetMapping(value = "/fanoutSend/{message}")
    public Object fanoutSend(@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout模式只往exchange里发送消息。分发到exchange下的所有queue
        rabbitTemplate.send("fanoutExchange", "", new Message(message.getBytes("UTF-8"), messageProperties));
        return "message sended : " + message;
    }


    //routing路由工作模式  交换机类型 direct
    @ApiOperation(value = "direct发送接口", notes = "发送到directExchange。exchange转发消息时,会往routingKey匹配的queue发送")
    @GetMapping(value = "/directSend/{routingKey}/{message}")
    public Object routingSend(@PathVariable("routingKey") String routingKey,@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {

        if (null == routingKey) {
            routingKey = "china.changsha";
        }
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout模式只往exchange里发送消息。分发到exchange下的所有queue
        rabbitTemplate.send("directExchange", routingKey, new Message(message.getBytes("UTF-8"), messageProperties));
        return "message sended : routingKey >" + routingKey + ";message > " + message;
    }


    //topic 工作模式   交换机类型 topic
    @ApiOperation(value = "topic发送接口", notes = "发送到topicExchange。exchange转发消息时,会往routingKey匹配的queue发送,*代表一个单词,#代表0个或多个单词。")
    @GetMapping(value = "/topicSend/{routingKey}/{message}")
    public Object topicSend(@PathVariable("routingKey") String routingKey,@PathVariable("message") String message) throws AmqpException, UnsupportedEncodingException {

        if (null == routingKey) {
            routingKey = "changsha.kf";
        }
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
        //fanout模式只往exchange里发送消息。分发到exchange下的所有queue
        rabbitTemplate.send("topicExchange", routingKey, new Message(message.getBytes("UTF-8"), messageProperties));
        return "message sended : routingKey >" + routingKey + ";message > " + message;
    }

}

模式配置

(1)直连模式
package com.fan.testsendupdatell.configs;



import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * HelloWorld rabbitmq第一个工作模式
 * 直连模式只需要声明队列,所有消息都通过队列转发。
 * 无需设置交换机
 */
@Configuration
public class HelloWorldConfig {

    @Bean
    public Queue setQueue() {
        return new Queue("helloWorldqueue");
    }

}

(2)工作队列模式

package com.fan.testsendupdatell.configs;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WorkConfig {

    //声明队列
    @Bean
    public Queue workQ1() {
        return new Queue("work_sb_mq_q");
    }
}

(3)发布订阅模式

package com.fan.testsendupdatell.configs;


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Fanout模式需要声明exchange,并绑定queue,由exchange负责转发到queue上。
 * 广播模式 交换机类型设置为:fanout
 */
@Configuration
public class FanoutConfig {

    //声明队列
    @Bean
    public Queue fanoutQ1() {
        return new Queue("fanout.q1");
    }
    @Bean
    public Queue fanoutQ2() {
        return new Queue("fanout.q2");
    }


    //声明exchange
    @Bean
    public FanoutExchange setFanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }


    //声明Binding,exchange与queue的绑定关系
    @Bean
    public Binding bindQ1() {
        return BindingBuilder.bind(fanoutQ1()).to(setFanoutExchange());
    }
    @Bean
    public Binding bindQ2() {
        return BindingBuilder.bind(fanoutQ2()).to(setFanoutExchange());
    }
}

(4)routing路由工作模式

package com.fan.testsendupdatell.configs;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
   路由模式|Routing模式   交换机类型:direct
*/
@Configuration
public class DirectConfig {

    //声明队列
    @Bean
    public Queue directQ1() {
        return new Queue("direct_sb_mq_q1");
    }
    @Bean
    public Queue directQ2() {
        return new Queue("direct_sb_mq_q2");
    }


    //声明exchange
    @Bean
    public DirectExchange setDirectExchange() {
        return new DirectExchange("directExchange");
    }

    //声明binding,需要声明一个routingKey
    @Bean
    public Binding bindDirectBind1() {
        return BindingBuilder.bind(directQ1()).to(setDirectExchange()).with("china.changsha");
    }
    @Bean
    public Binding bindDirectBind2() {
        return BindingBuilder.bind(directQ2()).to(setDirectExchange()).with("china.beijing");
    }

}

(5)topic 工作模式

package com.fan.testsendupdatell.configs;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/*
Topics模式  交换机类型 topic
* */
@Configuration
public class TopicConfig {

    //声明队列
    @Bean
    public Queue topicQ1() {
        return new Queue("topic_sb_mq_q1");
    }
    @Bean
    public Queue topicQ2() {
        return new Queue("topic_sb_mq_q2");
    }


    //声明exchange
    @Bean
    public TopicExchange setTopicExchange() {
        return new TopicExchange("topicExchange");
    }

    //声明binding,需要声明一个roytingKey
    @Bean
    public Binding bindTopicHebei1() {
        return BindingBuilder.bind(topicQ1()).to(setTopicExchange()).with("changsha.*");
    }
    @Bean
    public Binding bindTopicHebei2() {
        return BindingBuilder.bind(topicQ2()).to(setTopicExchange()).with("#.beijing");
    }
}

消费方

package com.fan.testsendupdatell.Concumer;

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

import java.util.concurrent.ConcurrentHashMap;


@Component
public class ConcumerReceiver {


    //直连模式的多个消费者,会分到其中一个消费者进行消费。类似task模式
    //通过注入RabbitContainerFactory对象,来设置一些属性,相当于task里的channel.basicQos
    @RabbitListener(queues = "helloWorldqueue")
    public void helloWorldReceive(String message) {

        System.out.println("helloWorld模式 received message : " + message);
    }
    //直连模式可以有多个消费者 ,但是只有一个消费者可以获取到消息,使用轮询方式来处理消息,消息不可以重复被消费
    @RabbitListener(queues = "helloWorldqueue")
    public void helloWorldReceive11(String message) {
        System.out.println("helloWorld模式 received message11 : " + message);
    }

    @RabbitListener(queues = "helloWorldqueue2")
    public void helloWorldReceive2(String message) {

        System.out.println("helloWorld模式 received message : " + message);
    }



    //工作队列模式
    @RabbitListener(queues = "work_sb_mq_q")
    public void wordQueueReceiveq1(String message) {

        System.out.println("工作队列模式1 received message : " + message);
    }

    @RabbitListener(queues = "work_sb_mq_q")
    public void wordQueueReceiveq2(String message) {

        System.out.println("工作队列模式2 received message : " + message);
    }


    //pub/sub模式进行消息监听
    @RabbitListener(queues = "fanout.q1")
    public void fanoutReceiveq1(String message) {

        System.out.println("发布订阅模式1received message : " + message);
    }

    @RabbitListener(queues = "fanout.q2")
    public void fanoutReceiveq2(String message) {

        System.out.println("发布订阅模式2 received message : " + message);
    }


    //Routing路由模式
    @RabbitListener(queues = "direct_sb_mq_q1")
    public void routingReceiveq1(String message) {

        System.out.println("Routing路由模式routingReceiveq11111 received message : " + message);
    }

    @RabbitListener(queues = "direct_sb_mq_q2")
    public void routingReceiveq2(String message) {

        System.out.println("Routing路由模式routingReceiveq22222 received message : " + message);
    }

    //topic 模式
    //注意这个模式会有优先匹配原则。例如发送routingKey=hunan.IT,那匹配到hunan.*(hunan.IT,hunan.eco),之后就不会再去匹配*.ITd
    @RabbitListener(queues = "topic_sb_mq_q1")
    public void topicReceiveq1(String message) {
        System.out.println("Topic模式 topic_sb_mq_q1 received message : " + message);
    }

    @RabbitListener(queues = "topic_sb_mq_q2")
    public void topicReceiveq2(String message) {
        System.out.println("Topic模式 topic_sb_mq_q2 received  message : " + message);
    }


}

结束

参考链接:

(2条消息) SpringBoot整合RabbitMQ:5种模式实战_萧 炎的博客-CSDN博客

你可能感兴趣的:(RabbitMQ,spring,boot,java-rabbitmq,rabbitmq)