SpringBoot+RabbitMQ操作消息队列(MQ)

SpringBoot + RabbitMQ 做延时队列

什么是RabbitMQ?
MQ全称为Message Queue,消息队列(MQ),是一种应用程序对应用程序的通讯方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。RabbitMQ是一种消息队列,用于程序间的通信。

为什么要使用 RabbitMQ?
在这里插入图片描述
RabbitMQ解决什么问题 ?
1:同步变异步。
2:解除服务之间的耦合度。
3:流量削锋

安装Erlang
Erlang官网:http://www.erlang.org/
安装:https://www.cnblogs.com/minily/p/7398445.html

安装RabbitMQ server
RabbitMQ官网:https://hub.docker.com/_/rabbitmq/
安装:https://blog.51cto.com/10836356/2082963

安装之后到本机的安装目录RabbitMQ Server\rabbitmq_server-3.7.18\sbin\下运行 cmd
我的安装路径是:G:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.18\sbin):

rabbitmq-plugins enable rabbitmq_management

rabbitmq-server -detached
在这里插入图片描述
如果运行成功如上图就可以测试http://localhost:15672/
会是这样的页面在这里插入图片描述
这里的账号,密码都是默认的:guest

开始搭建spring项目
项目结构:
在这里插入图片描述

添加依赖

	<!--RabbitMQ依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--生成get()set()方法-->
	<dependency>
	    <groupId>org.projectlombok</groupId>
	    <artifactId>lombok</artifactId>
	    <optional>true</optional>
	</dependency>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-devtools</artifactId>
	    <scope>runtime</scope>
	    <optional>true</optional>
	</dependency>

__这里不用添加mysql相关的依赖,因为没有和数据库连接;如果添加了 会报“未找到数据源”的错误信息__创建实体类Order

package com.example.springrabbitmqdemo.pojo;

import lombok.Data;

import java.io.Serializable;
@Data
public class Order implements Serializable {
    private static final long serialVersionUID = -2221214252163879885L;
    private String orderId;
    private Integer orderStatus;
    private String orderName;

}

工具类

package com.example.springrabbitmqdemo.tools;

import org.springframework.context.annotation.Configuration;

public class Constants {
    /**
     * 延迟队列 TTL 名称
     */
    public static final String ORDER_DELAY_QUEUE = "user.order.delay.queue";
    /**
     * DLX,dead letter发送到的 exchange
     * 延时消息就是发送到该交换机的
     */
    public static final String ORDER_DELAY_EXCHANGE = "user.order.delay.exchange";
    /**
     * routing key 名称
     * 具体消息发送在该 routingKey 的
     */
    public static final String ORDER_DELAY_ROUTING_KEY = "order_delay";
    public static final String ORDER_QUEUE_NAME = "user.order.queue";
    public static final String ORDER_EXCHANGE_NAME = "user.order.exchange";
    public static final String ORDER_ROUTING_KEY = "order";


    public static final  String QUEUE="direct_queue";
    public static final String TOPIC_QUEUE1 = "topic.queue1";
    public static final String TOPIC_QUEUE2 = "topic.queue2";
    public static final String TOPIC_EXCHANGE = "topic.exchange";
    public static final String FANOUT_EXCHANGE = "fanout.exchange";
}

声明和绑定队列到exchange的工作由消费者方完成

package com.example.springrabbitmqdemo.config;

import com.example.springrabbitmqdemo.tools.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 初始化queue exchange和queue及exchange之间的binding关系
 */
@Configuration
@Slf4j
public class DelayConfig {
    /**
     * 延迟队列配置
     * 1、params.put("x-message-ttl", 5 * 1000);
     * 第一种方式是直接设置 Queue 延迟时间 但如果直接给队列设置过期时间,这种做法不是很灵活,(当然二者是兼容的,默认是时间小的优先)
     * 2、rabbitTemplate.convertAndSend(book, message -> {
     * message.getMessageProperties().setExpiration(2 * 1000 + "");
     * return message;
     * });
     * 第二种就是每次发送消息动态设置延迟时间,这样我们可以灵活控制
     **/
    @Bean
    public Queue dalayQueue() {
        Map<String, Object> map = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        map.put("x-dead-letter-exchange", Constants.ORDER_EXCHANGE_NAME);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名
        map.put("x-dead-letter-routing-key", Constants.ORDER_ROUTING_KEY);
        return new Queue(Constants.ORDER_DELAY_QUEUE, true, false, false, map);
    }

    /**
     * 需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。
     * 这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,
     * 不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
     *
     * @return DirectExchange
     */
    @Bean
    public DirectExchange orderDirectExchange() {
        return new DirectExchange(Constants.ORDER_DELAY_EXCHANGE);
    }

    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(dalayQueue()).to(orderTopicExchange()).with(Constants.ORDER_DELAY_ROUTING_KEY);
    }

    @Bean
    public Queue orderQueue() {
        return new Queue(Constants.ORDER_QUEUE_NAME, true);
    }

    /**
     * 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。
     * 符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。
     **/
    @Bean

    public TopicExchange orderTopicExchange() {
        return new TopicExchange(Constants.ORDER_EXCHANGE_NAME);
    }

    @Bean
    public Binding orderBinding() {
        // 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(orderQueue()).to(orderTopicExchange()).with(Constants.ORDER_ROUTING_KEY);
    }

}

消费者生产消息

package com.example.springrabbitmqdemo.config;


import com.example.springrabbitmqdemo.pojo.Order;
import com.example.springrabbitmqdemo.tools.Constants;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.Date;

@Slf4j
@Component
public class DelayReceiver {
    @RabbitListener(queues = {Constants.ORDER_QUEUE_NAME})
    public void orderDelayQueue(Order order, Message message, Channel channel) {
        log.info("**********************************");
        //输出当前时间
        log.info("【orderDelayQueue 监听的消息】 - 【消费时间】 - [{}]- 【订单内容】 - [{}]", new Date(),  order.toString());
        //对状态status进行判断,以及把信息打应在控制台
        if (order.getOrderStatus() == 0) {
            order.setOrderStatus(2);
            log.info("【该订单未支付,取消订单】" + order.toString());
        } else if (order.getOrderStatus() == 1) {
            log.info("【该订单已完成支付】");
        } else if (order.getOrderStatus() == 2) {
            log.info("【该订单已取消】");
        }
        log.info("**********************************");
    }
}

生产者生产消息

package com.example.springrabbitmqdemo.config;

import com.example.springrabbitmqdemo.pojo.Order;
import com.example.springrabbitmqdemo.tools.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

@Slf4j
@Component
public class DelaySender {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendDelay(Order order) {
        log.info("【订单生成时间】" + new Date().toString() + "【1分钟后检查订单是否已经支付】" + order.toString());
        this.amqpTemplate.convertAndSend(Constants.ORDER_DELAY_EXCHANGE, Constants.ORDER_DELAY_ROUTING_KEY, order, message -> {
            //如果配置了 params.put("x-message-ttl", 5 * 1000);
            //那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
            message.getMessageProperties().setExpiration(1 * 1000 * 60 + "");
            return message;
        });
    }
}

各种模式

package com.example.springrabbitmqdemo.config;

import com.example.springrabbitmqdemo.tools.Constants;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {

    /**
     * Direct模式
     */
    @Bean
    public Queue directQueue() {
        // 第一个参数是队列名字, 第二个参数是指是否持久化
        return new Queue(Constants.QUEUE, true);
    }

    /**
     * Topic模式
     */
    @Bean
    public Queue topicQueue1() {
        return new Queue(Constants.TOPIC_QUEUE1);
    }

    @Bean
    public Queue topicQueue2() {
        return new Queue(Constants.TOPIC_QUEUE2);
    }

    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(Constants.TOPIC_EXCHANGE);
    }

    @Bean
    public Binding topicBinding() {
        return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("dovis.message");
    }

    @Bean
    public Binding topicBinding2() {
        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("dovis.#");
    }

    /**
     * Fanout模式
     * Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
     */

    @Bean

    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(Constants.FANOUT_EXCHANGE);
    }

    @Bean
    public Binding fanoutBinding1() {
        return BindingBuilder.bind(topicQueue1()).to(fanoutExchange());
    }

    @Bean
    public Binding fanoutBinding2() {
        return BindingBuilder.bind(topicQueue2()).to(fanoutExchange());
    }
}

监听器

package com.example.springrabbitmqdemo.config;

import com.example.springrabbitmqdemo.pojo.User;
import com.example.springrabbitmqdemo.tools.Constants;
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 = Constants.QUEUE)
    public void receiverDirectQueue(User user) {
        log.info("【receiverDirectQueue监听到消息】" + user.toString());
    }

    // queues是指要监听的队列的名字

    @RabbitListener(queues = Constants.TOPIC_QUEUE1)
    public void receiveTopic1(User user) {
        log.info("【receiveTopic1监听到消息】" + user.toString());
    }

    @RabbitListener(queues = Constants.TOPIC_QUEUE2)
    public void receiveTopic2(User user) {
        log.info("【receiveTopic2监听到消息】" + user.toString());
    }
}

测试类Testcontroller

@GetMapping("/sendDelay")
public Object sendDelay() {
    Order order = new Order();
    //状态
    order.setOrderStatus();
    //id
    order.setOrderId();
    //商品名称
    order.setOrderName();
    return "ok";
}

配置application类

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest~

启动项目
在这里插入图片描述
等待一分钟------因为在DelaySender类message.getMessageProperties().setExpiration(1 * 1000 * 60 + “”);是一分钟
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值