消息中间件RabbitMQ在SpringBoot中的使用

版权声明:欢迎转载大宇的博客,转载请注明出处: https://blog.csdn.net/yanluandai1985/article/details/89094190

安装RabbitMQ

         参考链接:windows下 安装 rabbitMQ 及操作常用命令

                           RabbitMQ入门(一)——RabbitMQ的安装以及使用(Windows环境下)

一、导入依赖

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

二、编写配置文件

         使用默认的端口,并且使用guest帐号进行登录。

spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

三、创建两个队列

        我们来创建俩个队列,队列A和队列B。

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

@Configuration
public class RabbitMqConfig {
    public static final String QUEUE_NAME_A = "queueA";
    public static final String QUEUE_NAME_B = "queueB";

    @Bean
    public Queue queue1() {
        return new Queue(QUEUE_NAME_A);
    }

    @Bean
    public Queue queue2() {
        return new Queue(QUEUE_NAME_B);
    }
}

四、创建接收者

        接收者相当于就是消息的消费者,它从队列里面获取消息。

        接收者对象使用@RabbitListener注解来说明它接收的是哪个队列的消息。

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

@Component
@RabbitListener(queues = "queueA")
public class Receiver1 {

    private Logger logger = LoggerFactory.getLogger("Receiver1");

    @RabbitHandler
    public void receiver(String msg) {
        System.out.println("接收者1号获取到数据" + msg);
    }
}

五、向单个接收者发送消息

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void send() {
        String message = "Hello world";
        String receiver = "queueA";
        amqpTemplate.convertAndSend(receiver, message);
    }

}

        我们向队列queueA发送一个消息"hellow world"。然后监听这个队列的接收者是Receiver1类。所以执行 Receiver1类的使用@RabbitHandler注解的方法进行处理。成功在控制台上打印出数据。

六、向多个接收者发送消息

        再创建一个接收队列queueA的接收者:接收者2号。

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

@Component
@RabbitListener(queues = "queueA")
public class Receiver2 {

    private Logger logger = LoggerFactory.getLogger("Receiver2");

    @RabbitHandler
    public void receiver(String msg) {
        System.out.println("接收者2号获取到数据" + msg);
    }
}

        再次测试发送消息,这次循环发送消息。消息向队列queueA发送,监听queueA队列的两个接收者:接收者1号和接收者2号,会自动负载均衡。并且每个消息只会被接收一次。

    @Test
    public void sendManyTimes() {
        String message = "Hello world";
        String receiver = "queueA";
        for (int i = 0; i < 10; i++) {
            amqpTemplate.convertAndSend(receiver, message + i);
        }

    }

七、向多个队列发送消息

         修改接收者2号,让它监听队列B。修改后的代码为

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

@Component
@RabbitListener(queues = "queueB")
public class Receiver2 {

    private Logger logger = LoggerFactory.getLogger("Receiver2");

    @RabbitHandler
    public void receiver(String msg) {
        System.out.println("接收者2号获取到数据" + msg);
    }
}

        然后我们再来测试一下,分别向队列A和队列B发送5条消息。监听队列A的接收者1号应该会收到5条消息,监听队列B的接收者2号也会接收到5条消息。

        根据结果来看,这次每条消息都分别发送给了接收者1号和接收者2号。

    @Test
    public void sendManyTimes2() {
        String message = "Hello world";
        String receiverA = "queueA";
        String receiverB = "queueB";
        for (int i = 0; i < 5; i++) {
            amqpTemplate.convertAndSend(receiverA, message + i);
            amqpTemplate.convertAndSend(receiverB, message + i);
        }
    }

八、使用Topic Exchange广播

        Topic Exchange是RabbitMQ中最灵活的一种方式,它能够根据routing_key自由的绑定不同的队列。

        创建一个新的配置类,代码如下。创建了两个队列和一个路由器。

         路由器如果接收到的路由键是topic.message,那么就把消息发送到queueMessage队列。监听queueMessage队列的接收者就能收到消息了。

        路由器如果接收到的路由键是topic开头的,那么就把消息发送到queueMessages队列。监听queueMessages队列的接收者就能收到消息了。

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;


/**
 * @author jay.zhou
 * @date 2019/4/8
 * @time 15:16
 */
@Configuration
public class RabbitMqTopicConfig {
    /**
     * message队列名
     */
    final String message = "topic.message";

    /**
     * messages队列名
     */
    final String messages = "topic.messages";


    /**
     * 初始化一个叫topic.message的队列
     */
    @Bean
    public Queue queueMessage() {
        return new Queue(message);
    }


    /**
     * 初始化一个叫topic.messages的队列
     */
    @Bean
    public Queue queueMessages() {
        return new Queue(messages);
    }

    /**
     * 创建一个叫exchange的路由器
     * 路由器负责分发消息到指定的队列
     */
    @Bean
    TopicExchange exchange() {
        return new TopicExchange("exchange");
    }

    /**
     * 约定大于配置
     * 这个方法的参数queueMessage就是本类中queueMessage()方法的返回值
     * 这个方法的参数exchange就是本类中exchange()方法的返回值
     * 如果发送者投递的路由键是:topic.message,那么就把数据发送到queueMessage队列中
     */
    @Bean
    Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
        return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
    }

    /**
     * 约定大于配置
     * 这个方法的参数queueMessage就是本类中queueMessage()方法的返回值
     * 这个方法的参数exchange就是本类中exchange()方法的返回值
     * 如果发送者投递的路由键是:topic.#,即路由键使用topic开头的
     * 那么就把数据发送到queueMessages队列中
     */
    @Bean
    Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
        //这里的#表示零个或多个词。
        return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
    }
}

        接着,我们创建接收者。

        接收者1号监听topic.message的队列。

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

@Component
@RabbitListener(queues = "topic.message")
public class TopicReceiver1 {
    private Logger logger = LoggerFactory.getLogger("TopicReceiver1");

    @RabbitHandler
    public void process(String msg) {
        System.out.println("接收者1号接收到信息:" + msg);
    }
}

        接收者2号监听topic.messages的队列。

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

@Component
@RabbitListener(queues = "topic.messages")
public class TopicReceiver2 {
    private Logger logger = LoggerFactory.getLogger("TopicReceiver2");

    @RabbitHandler
    public void process(String msg) {
        System.out.println("接收者2号接收到信息:" + msg);
    }
}

         最后,创建测试方法。

         数据message交给交换器exchange,交换器根据路由键route_key,去配置类中匹配符合要求的队列。
         监听符合要求队列queue的接收者将会收到消息。

    @Test
    public void test2() {
        String message = "hello world";
        System.out.println("发送者说 : " + message);
        String exchangeName = "exchange";
        String route_key= "topic.message";
        //数据message交给交换器exchange,交换器将其投递到队列中
        //监听这个队列的接收者将会收到消息
        this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
    }

         我们发送的路由键的名字是topic.message,所以上图中的红色框框的配置。第一个红框表示:消息的路由键如果是"topic.message"的消息,交换机将会把消息投递到queueMessage队列中。第二个红框表示:消息的路由键如果已topic开头,那么路由器将会把这个消息投递到queueMessages队列。所以监听这些队列的接收者将会收到消息。接收者1号监听的是topic.message队列。接收者2号监听的是topic.messages队列。所以,1号和2号都收到了消息。

          最后再测一下,如果把消息的路由键修改为topic开头的数据,测试接收者1号能不能收到数据。预期是不能收到。

          路由键以topic开头,后面的可以是任何字符串。发送出去的时候,消息将会被路由器投递到queueMessages队列。监听queueMessages队列的接收者是2号。所以运行结果是2号收到消息。

    @Test
    public void test3() {
        String message = "hello world";
        System.out.println("发送者说 : " + message);
        String exchangeName = "exchange";
        String route_key = "topic.asdkjqwidjasldkj";
        //数据message交给交换器exchange,交换器将其投递到队列中
        //监听这个队列的接收者将会收到消息
        this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
    }

九、Fanout Exchange(订阅模式)

        topic类型的广播形式,路由器根据消息的路由键,最后将消息投递到指定的队列中。

        fanout类型的广播形式,

        首先创建两个队列,名字是queueA和queueB。然后再创建一个Fanout路由器。这种类型的路由器,会把收到的消息,直接投递到绑定它的所有的队列中,不用路由键,直接不BB,投递就完事了。接收者可从队列中获取消息。

        

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


@Configuration
public class RabbitMqFanoutConfig {



    /**
     * 初始化一个叫queueA的队列
     */
    @Bean
    public Queue queueA() {
        return new Queue("queueA");
    }


    /**
     * 初始化一个叫queueB的队列
     */
    @Bean
    public Queue queueB() {
        return new Queue("queueB");
    }

    /**
     * 创建一个叫fanoutExchange的路由器
     * 路由器负责分发消息到指定的队列
     */
    @Bean
    FanoutExchange exchange() {
        return new FanoutExchange("fanoutExchange");
    }


    /**
     * 直接把队列绑定到交换机身上,不用路由键
     * 也就是说,交换机把消息直接投递到绑定它的队列中
     * 不需要路由键
     */
    @Bean
    Binding bindingExchangeMessage(Queue queueA, FanoutExchange exchange) {
        return BindingBuilder.bind(queueA).to(exchange);
    }

    @Bean
    Binding bindingExchangeMessages(Queue queueB, FanoutExchange exchange) {
        return BindingBuilder.bind(queueB).to(exchange);
    }
}

        我们再来创建两个接收者。接收者1号接收来自队列A的消息,接收者2号接收来自队列B的消息。

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

@Component
@RabbitListener(queues = "queueA")
public class FanoutReceiver1 {
    private Logger logger = LoggerFactory.getLogger("FanoutReceiver1");

    @RabbitHandler
    public void process(String msg) {
        System.out.println("接收者1号接收到信息:" + msg);
    }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "queueB")
public class FanoutReceiver2 {
    private Logger logger = LoggerFactory.getLogger("FanoutReceiver2");

    @RabbitHandler
    public void process(String msg) {
        System.out.println("接收者1号接收到信息:" + msg);
    }
}

        最后,编写测试文件。

        注意,测试的时候删除掉RabbitMqTopicConfig类,我测试的时候发现好像不能同时创建Topic类型和Fanout类型的交换机。为了不影响测试,得删除掉Topic类型的交换机。

        因为消息发送给了Fanout交换机,所以交换机会把消息发送给绑定它的队列,分别是队列A和队列B。接收者1号会从队列A中获取消息,接收者2号会从队列B中获取消息。最后运行的结果是:接收者1号和2号都会接收到消息。

    @Test
    public void fanoutMessage() {
        String message = "hello world";
        //路由键在Fanout广播形式中无效
        String route_key = "";
        System.out.println("发送者说 : " + message);
        String exchangeName = "fanoutExchange";
        //数据message交给交换器exchange,交换器将其投递到队列中
        //监听这个队列的接收者将会收到消息
        this.amqpTemplate.convertAndSend(exchangeName, route_key, message);
    }

         

猜你喜欢

转载自blog.csdn.net/yanluandai1985/article/details/89094190