SpringBoot使用RabbitMQ消息队列

RabbitMQ简介

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

RabbitMQ基本概念


1.Message

消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。

2.Publisher

消息的生产者,也是一个向交换器发布消息的客户端应用程序。

3.Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
4.Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
5.Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
6.Connection
网络连接,比如一个TCP连接。
7.Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
8.Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
9.Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
10.Broker
表示消息队列服务器实体。

Exchange 类型

Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。下面只讲前三种模式。

1.Direct模式

消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配

2.Topic模式

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“*”。#匹配0个或多个单词,*匹配一个单词。

3.Fanout模式

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。


SpringBoot整合RabbitMQ

在  pom.xml  中添加  spring-boot-starter-amqp 的依赖
       <dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

在 application.yml文件中配置rabbitmq相关内容

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest


使用Direct模式

1.配置队列

package com.lzc.rabbitmq.config;

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

@Configuration
public class RabbitMQConfig {
    static final String QUEUE = "direct_queue";
    /**
     * Direct模式
     * @return
     */
    @Bean
    public Queue directQueue() {
        // 第一个参数是队列名字, 第二个参数是指是否持久化
        return new Queue(QUEUE, true);
    }
}

2.创建一个User实体类

package com.lzc.rabbitmq.dataobject;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = -1262627851741431084L;

    private String userId;

    private String name;
}

3.接收者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Receiver {

    // queues是指要监听的队列的名字
    @RabbitListener(queues = RabbitMQConfig.QUEUE)
    public void receiverDirectQueue(User user) {
        log.info("【receiverDirectQueue监听到消息】" + user.toString());
    }

}

4.发送者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Sender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendDirectQueue() {
        User user = new User();
        user.setUserId("123456");
        user.setName("lizhencheng");
        log.info("【sendDirectQueue已发送消息】");
         // 第一个参数是指要发送到哪个队列里面, 第二个参数是指要发送的内容
        this.amqpTemplate.convertAndSend(RabbitMQConfig.QUEUE, user);
    }

}

5.测试,访问http://localhost:8080/sendDirectQueue,查看日志输出

package com.lzc.rabbitmq.controller;

import com.lzc.rabbitmq.config.Sender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private Sender sender;

    @GetMapping("/sendDirectQueue")
    public Object sendDirectQueue() {
        sender.sendDirectQueue();
        return "ok";
    }
}

6.日志输出

2018-06-18 01:18:54.901  INFO 8772 --- [io-8080-exec-10] com.lzc.rabbitmq.config.Sender           : 【sendDirectQueue已发送消息】
2018-06-18 01:18:54.997  INFO 8772 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiverDirectQueue监听到消息】User(userId=123456, name=lizhencheng)

注意:发送者与接收者的Queue名字一定要相同,否则接收收不到消息

使用Topic模式

1.配置队列

package com.lzc.rabbitmq.config;

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;

@Configuration
public class RabbitMQConfig {
    
    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";
    public static final String TOPIC_EXCHANGE = "topic.exchange";

    /**
     * Topic模式
     * @return
     */
    @Bean
    public Queue topicQueue1() {
        return new Queue(TOPIC_QUEUE1);
    }
    @Bean
    public Queue topicQueue2() {
        return new Queue(TOPIC_QUEUE2);
    }
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(TOPIC_EXCHANGE);
    }
    @Bean
    public Binding topicBinding1() {
        return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("lzc.message");
    }
    @Bean
    public Binding topicBinding2() {
        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("lzc.#");
    }

}

2.创建一个User实体类(和上面一样)

3.接收者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Receiver {
    // queues是指要监听的队列的名字
    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE1)
    public void receiveTopic1(User user) {
        log.info("【receiveTopic1监听到消息】" + user.toString());
    }
    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE2)
    public void receiveTopic2(User user) {
        log.info("【receiveTopic2监听到消息】" + user.toString());
    }

}

4.发送者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Sender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendTopic() {
        User user1 = new User();
        user1.setUserId("123456");
        user1.setName("lizhencheng");

        User user2 = new User();
        user2.setUserId("456789");
        user2.setName("张三");

        log.info("【sendTopic已发送消息】");
        // 第一个参数:TopicExchange名字
        // 第二个参数:Route-Key
        // 第三个参数:要发送的内容
        this.amqpTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "lzc.message", user1 );
        this.amqpTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "lzc.lzc", user2);
    }

}

5.测试,访问http://localhost:8080/sendTopic,查看日志输出

package com.lzc.rabbitmq.controller;

import com.lzc.rabbitmq.config.Sender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private Sender sender;

    @GetMapping("/sendTopic")
    public Object sendTopic() {
        sender.sendTopic();
        return "ok";
    }
}

6.日志输出

2018-06-18 09:48:24.983  INFO 6504 --- [nio-8080-exec-1] com.lzc.rabbitmq.config.Sender           : 【sendTopic已发送消息】
2018-06-18 09:48:25.048  INFO 6504 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiveTopic2监听到消息】User(userId=123456, name=lizhencheng)
2018-06-18 09:48:25.048  INFO 6504 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiveTopic1监听到消息】User(userId=123456, name=lizhencheng)
2018-06-18 09:48:25.049  INFO 6504 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiveTopic2监听到消息】User(userId=456789, name=张三)

由日志记录可以看出lzc.message可以被receiveTopic1和receiveTopic2所接收,而lzc.lzc只能被receiveTopic2接收。

使用Fanout模式

1.配置队列

package com.lzc.rabbitmq.config;

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

@Configuration
public class RabbitMQConfig {
    
    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";

    public static final String FANOUT_EXCHANGE = "fanout.exchange";


    /**
     * 这里为了方便就没有创建新的队列了,直接使用topic时所创建的队列。
     * @return
     */
    @Bean
    public Queue topicQueue1() {
        return new Queue(TOPIC_QUEUE1);
    }
    @Bean
    public Queue topicQueue2() {
        return new Queue(TOPIC_QUEUE2);
    }
    

    /**
     * Fanout模式
     * Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
     * @return
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(FANOUT_EXCHANGE);
    }
    @Bean
    public Binding fanoutBinding1() {
        return BindingBuilder.bind(topicQueue1()).to(fanoutExchange());
    }
    @Bean
    public Binding fanoutBinding2() {
        return BindingBuilder.bind(topicQueue2()).to(fanoutExchange());
    }

}

2.创建一个User实体类(和上面一样)

3.接收者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Receiver {

    // queues是指要监听的队列的名字
    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE1)
    public void receiveTopic1(User user) {
        log.info("【receiveTopic1监听到消息】" + user.toString());
    }
    @RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE2)
    public void receiveTopic2(User user) {
        log.info("【receiveTopic2监听到消息】" + user.toString());
    }

}

4.发送者

package com.lzc.rabbitmq.config;

import com.lzc.rabbitmq.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Sender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendFanout() {
        User user = new User();
        user.setUserId("123456");
        user.setName("lizhencheng");
        log.info("【sendFanout已发送消息】");
        // 注意, 这里的第2个参数为空。
        // 因为fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,
        // 每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上
        this.amqpTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE, "", user );
    }

}

5.测试,访问http://localhost:8080/sendFanout,查看日志输出

package com.lzc.rabbitmq.controller;

import com.lzc.rabbitmq.config.Sender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private Sender sender;

    @GetMapping("/sendFanout")
    public Object sendFanout() {
        sender.sendFanout();
        return "ok";
    }
}

6.日志输出

2018-06-18 02:04:47.817  INFO 5848 --- [nio-8080-exec-1] com.lzc.rabbitmq.config.Sender           : 【sendFanout已发送消息】
2018-06-18 02:04:47.845  INFO 5848 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiveTopic1监听到消息】User(userId=123456, name=lizhencheng)
2018-06-18 02:04:47.845  INFO 5848 --- [cTaskExecutor-1] com.lzc.rabbitmq.config.Receiver         : 【receiveTopic2监听到消息】User(userId=123456, name=lizhencheng)

由日志输出可以看出,两个接收者都接收到了消息,因为交换机FANOUT_EXCHANGE绑定了两个队列。


查看完整代码

  • 11
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
### 回答1: 在 Spring Boot 中,你可以通过 RabbitMQ 的 `x-delayed-message` 插件来实现延时队列,而不需要使用额外的插件或库。下面是实现步骤: 1. 添加依赖 在 `pom.xml` 文件中添加 RabbitMQ 的依赖: ```xml <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.3.12.RELEASE</version> </dependency> ``` 2. 配置 RabbitMQ 的 `x-delayed-message` 插件 在 RabbitMQ 中,你需要先安装 `x-delayed-message` 插件。你可以通过 `rabbitmq-plugins` 命令来安装插件: ``` rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` 或者,你可以在 `rabbitmq.conf` 文件中添加以下配置,然后重启 RabbitMQ: ``` plugins.rabbitmq_delayed_message_exchange = {git, "https://github.com/rabbitmq/rabbitmq-delayed-message-exchange", {branch, "master"}} ``` 3. 配置 RabbitMQ 的连接信息 在 `application.properties` 中添加 RabbitMQ 的连接信息: ```properties spring.rabbitmq.host=your-rabbitmq-host spring.rabbitmq.port=5672 spring.rabbitmq.username=your-rabbitmq-username spring.rabbitmq.password=your-rabbitmq-password ``` 4. 定义队列和交换器 在 Spring Boot 中,你可以使用 `@Configuration` 和 `@Bean` 注解来定义队列和交换器。下面是一个例子: ```java @Configuration public class RabbitConfig { @Bean public Queue delayedQueue() { return QueueBuilder.durable("delayed.queue") .withArgument("x-dead-letter-exchange", "normal.exchange") .withArgument("x-dead-letter-routing-key", "normal.routingkey") .build(); } @Bean public CustomExchange delayedExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed.exchange", "x-delayed-message", true, false, args); } @Bean public Binding binding() { return BindingBuilder.bind(delayedQueue()) .to(delayedExchange()) .with("delayed.routingkey") .noargs(); } } ``` 在上面的例子中,我们定义了一个 `delayedQueue` 队列,它的死信交换器是 `normal.exchange`,死信路由键是 `normal.routingkey`。我们还定义了一个 `delayedExchange` 交换器,它的类型是 `x-delayed-message`,并将 `x-delayed-type` 属性设置为 `direct`。最后,我们将 `delayedQueue` 队列绑定到 `delayedExchange` 交换器上,并使用路由键 `delayed.routingkey`。 5. 发送延时消息 你可以使用 `RabbitTemplate` 类来发送消息到 `delayedQueue` 队列。在发送消息时,你需要将消息的 `headers` 属性设置为 `x-delay`,并将值设置为消息的延时时间(单位为毫秒)。 ```java @Autowired private RabbitTemplate ### 回答2: 在Spring Boot中实现RabbitMQ延时队列需要以下几个步骤: 1. 首先,我们需要定义一个交换机(Exchange),用于将消息发送到延时队列中。可以使用DirectExchange、TopicExchange或FanoutExchange等不同类型的交换机。交换机的类型根据具体的业务需求而定。 2. 接下来,我们需要定义两个队列,一个为延时队列,另一个为业务队列。延时队列用于接收需要延时处理的消息,业务队列用于接收延时队列中处理完成的消息。 3. 创建并配置消息发送和接收的相关组件。使用RabbitTemplate来发送消息到延时队列,创建一个消费者来接收延时队列中的消息并处理。 4. 在消息发送时,可以通过给消息设置不同的过期时间来实现延时功能。在发送消息时,将消息携带的延时时间设置为过期时间,然后发送到延时队列中。 5. 在消费者中,监听业务队列,当接收到延时队列中的消息时,进行相应的处理,例如发送邮件、生成报表等。 这样就实现了RabbitMQ延时队列的功能。通过设置消息的过期时间,可以控制消息何时被消费。延时队列可以在某个特定的时间点将消息转发到业务队列,完成后续处理。Spring Boot提供了简单而强大的集成,可以轻松实现延时队列的功能。 ### 回答3: 实现RabbitMQ延时队列的核心思想是利用RabbitMQ的插件(x-delayed-message)和Spring Boot的消息中间件(RabbitTemplate)结合使用。 首先,确保在RabbitMQ服务中安装了插件。在RabbitMQ的安装目录下,执行以下命令: ``` rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` 接下来,在Spring Boot项目的pom.xml文件中添加RabbitMQ的依赖: ```xml <dependencies> <!-- RabbitMQ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> </dependencies> ``` 然后,创建一个配置类,用于连接RabbitMQ服务和创建延时队列: ```java @Configuration public class RabbitMQConfig { @Autowired private Environment env; @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setAddresses(env.getProperty("spring.rabbitmq.addresses")); connectionFactory.setUsername(env.getProperty("spring.rabbitmq.username")); connectionFactory.setPassword(env.getProperty("spring.rabbitmq.password")); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory()); rabbitTemplate.setMessageConverter(jsonMessageConverter()); return rabbitTemplate; } @Bean public MessageConverter jsonMessageConverter() { return new Jackson2JsonMessageConverter(); } @Bean public Exchange delayedExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed-exchange", "x-delayed-message", true, false, args); } @Bean public Queue delayedQueue() { return new Queue("delayed-queue", true); } @Bean public Binding delayedBinding() { return BindingBuilder.bind(delayedQueue()).to(delayedExchange()).with("delayed-routing-key").noargs(); } } ``` 在上述代码中,我们创建了一个自定义的Exchange,将其类型设置为"x-delayed-message",并创建了一个延时队列,将其绑定在这个Exchange上。这样,消息发送到这个Exchange时,会根据消息中的延时时间属性进行延时处理。 最后,我们可以通过RabbitTemplate发送延时消息: ```java @Service public class RabbitMQService { @Autowired private RabbitTemplate rabbitTemplate; public void sendDelayedMessage(String message, int delayTime) { rabbitTemplate.convertAndSend("delayed-exchange", "delayed-routing-key", message, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { message.getMessageProperties().setHeader("x-delay", delayTime); return message; } }); } } ``` 在上述代码中,我们通过rabbitTemplate将消息发送到名为"delayed-exchange"的Exchange上,并设置消息的延时时间属性"x-delay"。最后,通过"convertAndSend"方法发送消息。 以上就是使用Spring Boot实现RabbitMQ延时队列的简单示例。通过这种方式,我们可以轻松地实现消息的延时处理,使得系统更加灵活和高效。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值