CentOS7下安装RabbitMQ,并使用Spring Boot实现一个简单的延迟队列(小白教程,附源码)

2 篇文章 0 订阅
1 篇文章 0 订阅

笔者在安装RabbitMQ时发现大部分博客记录的安装复杂且版本固定且过时,或复制粘贴缺胳膊少腿;故写下此博客,希望有需要的同学可以少走些弯路。
Win10 + Linux CentOS7
笔者RabbitMQ版本3.8.16,Java版本11,Spring Boot版本2.4.5。
使用win10系统去连接服务器不需要安装其它任何软件。
服务器安装使用root用户,使用rpm包。
本文宗旨操作简单化,描述详细化 。
项目源码:https://github.com/MuweiLaw/RabbitMQ.git
项目源码分为两个服务,一个生产者一个消费者,适用于分布式架构。

一.环境搭建

1-安装包和插件准备

1.1在本地新建空文件夹存放rpm安装包

在PC上新建一个文件夹用来存放安装包
例如我在E盘上新建一个空文件夹,路径为E:\upload
新建空文件夹

1.2下载RabbitMQ

进入 RabbitMQ下载地址,下载RabbitMQ的包。
笔者的Linux版本均为CentOS7,故下载图中蓝框版本
蓝色框上方为CentOS8版本
linux7需要下载的图片截图
点击下载放入到我们刚才建好的文件夹中

1.3下载RabbitMQ的延迟插件

进入延迟插件下载地址,在页面使用Ctrl+F直接搜索rabbitmq_delayed_message_exchange即可找到我们需要下载的插件
插件下载入口
下载和RabbitMQ配套的版本
下载v3.8延迟插件

1.4下载ErLang

RabbitMQ是基于Erlang语言开发,so在安装RabbitMQ之前,需要先安装Erlang,这里使用RabbitM
Q团队提供的Erlang依赖包。
进入Erlang下载页
Erlang下载
点击下载放入到我们刚才建好的文件夹中

2-上传安装包和插件到服务器

2.1上传准备

此时我们已经下载好了3个包并放在了E:\upload文件夹下
在这里插入图片描述

2.2上传本地文件到服务器

回到电脑桌面
使用快捷键win+R
在弹出的窗口输入cmd
回车或确认后打开windows运行命令行窗口,笔者的字体颜色是绿色(人生在世总要带点绿哈哈哈),大部分同学的字体颜色是白色命令行窗口
输入命令sftp user@host建立sftp连接,user是你的Linux账户名,host是你的Linux系统对外的IP地址。注意,密码不会显示,带有小键盘的同学尽量不要用小键盘。输入密码后回车显示
Connected to user@host.
则表示连接成功。如下:
在这里插入图片描述
连接成功后输入命令lcd E:\upload
再输入命令lls
就能看到我们之前放在 E:\upload 里的三个文件
查看本地文件
好了,现在我们开始本地上传这三个文件,put 加上对应的文件名称
put erlang-23.3.2-1.el7.x86_64.rpm
put rabbitmq_delayed_message_exchange-3.8.0.ez
put rabbitmq-server-3.8.16-1.el7.noarch.rpm
在这里插入图片描述
再接着,我们把这三个文件保存到服务器,get 加上对应的文件名称
get erlang-23.3.2-1.el7.x86_64.rpm
get rabbitmq_delayed_message_exchange-3.8.0.ez
get rabbitmq-server-3.8.16-1.el7.noarch.rpm
服务器报存文件
我们使用命令ls查看服务器是否保存这三个文件
使用命令pwd知道服务器当前保存的文件夹为 /root
如下:查看服务器保存文件
最后,我们使用Ctrl+C关闭sftp连接

2.3注意事项

IP查看方式:

1.虚拟机: 使用ifconfig命令
在这里插入图片描述
2.云服务器:在云服务器控制台上找到服务器的公网IP,笔者是一台最低配阿里云ECS服务器,年费用不超过100软妹币,满足日常学习开发使用,其它高配云服务器享受同等折扣,链接戳我直达

查看云服务器IP

第一次使用sftp传输文件回有如下提示
第一次使用sftp

3-安装

3.1使用win10自带ssh连接服务器

使用Xshell,PuTTY,CRT等ssh工具的可以跳过这一步

打开windows运行命令行
输入ssh user@host这里user依旧是你的Linux账户名,host依旧是你的Linux系统对外的IP地址。密码不显示,输入密码时尽量不要使用小键盘。
shh连接图示我们已经通过ssh用root账号连接上了我们的服务器,接下来我们将正式在服务器搭建环境

3.2正式搭建

登录到服务器成功之后
使用 ls 即可查看我们之前通过sftp传输的三个文件。ssh登录后查看文件

依次执行如下命令:

rpm -ivh erlang-23.3.2-1.el7.x86_64.rpm 安装erlang依赖
安装erlang依赖
yum -y install socat 安装socat
安装socat
rpm -ivh rabbitmq-server-3.8.16-1.el7.noarch.rpm 安装RabbitMQ 安装RabbitMQ

接下来我们启动RabbitMQ

使用命令 systemctl start rabbitmq-server 稍等几秒钟,正常启动如下,没有任何提示启动
systemctl enable rabbitmq-server 设置RabbitMQ开机自启
rabbitmq-plugins enable rabbitmq_management 启用web控制台
设置自启和web控制台

开放端口(防火墙已关闭的同学可以跳过这一小步)

firewall-cmd --zone=public --add-port=5672/tcp --permanent 开放客户端端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent 开放web控制台端口
firewall-cmd --reload 重启防火墙
开放端口
需要注意一点的是,使用阿里云服务器的同学还需要配置安全组云服务器配置安全组

尝试登录web控制台

RabbitMQ默认的账号用户名和密码都是guest
在浏览器输入 http://host:15672/ 即可访问web控制台,这里的 host 依旧是你的服务器对外IP。
例如笔者的web控制台的Url为 http://10.1.2.4:15672/
默认情况下,RabbitMQ的默认的guest用户只允许本机访问,当我们在web控制台输入账号密码后会发现如下图所示登录web控制台

所以我们还需要让guest用户能够远程访问

cd /etc/rabbitmq 进入到 etc/rabbitmq 目录下
touch rabbitmq.config 创建名为 rabbitmq.config 的配置文件
创建配置文件
vi rabbitmq.config 编辑配置文件编辑配置文件
按下回车键之后效果如下:注意红框内描述变化编辑中1
此时我们按下键盘 “ i ” 键,进行编辑,注意左下角变化,表示已进入编辑状态
编辑2
输入 [{rabbit, [{loopback_users, []}]}].
编辑3
再按下键盘“ Esc ”键退出编辑模式编辑4键盘依次按下 :wq
再按下回车键后保存并退出,注意此处冒号是英文冒号
编辑5
systemctl restart rabbitmq-server重启RabbitMQ,等待数秒
重启

再次访问web控制台

重新输入guest账号密码即可实现登录在这里插入图片描述

安装延迟插件

cd /usr/lib/rabbitmq/lib 先进入lib目录,
再使用 ls 查看RabbitMQ安装目录名称,因为版本因素,各位同学此处的目录名称可能也存在不同
查看文件夹
好了,知道黄色框内该版本的安装目录名称之后
cd rabbitmq_server-3.8.16/plugins 我们直接进入插件库文件夹内
ls 查看,后缀 .ez 的文件都是RabbitMQ自带的插件查看自带插件
cp /root/rabbitmq_delayed_message_exchange-3.8.0.ez ./ 我们将先前保存在 /root 目录下的延迟插件 rabbitmq_delayed_message_exchange-3.8.0.ez 拷贝到当前目录。
ls 可以查看到,我们的延迟插件已经拷贝到当前文件夹了
查看插件是否拷贝到插件库

现在我们要启用插件了

cd …/sbin 进入sbin目录(注意注意注意!!CSDN的bug,导致此处命令高亮区域显示了三个“ . ”,下图是两个“ . ”,直接复制粘贴时注意)
rabbitmq-plugins enable rabbitmq_delayed_message_exchange 启用插件成功后就可以看到如下信息启用插件信息
再进入到我们的web控制台,哎哟新增了一个交换机类型可选项 x-delayed-messageweb控制台新增交换机类型

环境搭建大功告成!!!

二.代码实现一个简单的订单超时自动取消功能

项目源码:https://github.com/MuweiLaw/RabbitMQ.git

1-代码编辑

笔者用的IDE是intellij IDEA,新建两个Spring Initializr项目
项目1:mq_producer
项目2:mq_consumer

两个项目的结构类似,如下:
项目结构

首先我们需要在两个项目中的pom.xml文件中都添加这3个依赖;

		<!--消息队列相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--    JSON支持,消息序列化用到    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
        <!--    测试    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

之后在application.properties添加RabbitMQ的相关配置;

mq_producer:

server.port=52320

spring.application.name=Spring-boot-rabbitmq
spring.rabbitmq.host=10.1.2.4
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#开启confirm 确认机制,如果对异步消息需要回调必须设置为correlated
#spring.rabbitmq.publisher-confirms=true   已过时,下行替代
spring.rabbitmq.publisher-confirm-type=correlated

mq_consumer:

server.port=52321

spring.rabbitmq.host=10.1.2.4
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.publisher-confirm-type=correlated

接下来创建RabbitMQ的Java配置,主要用于配置交换机、队列和绑定关系,两个项目的配置相同,如下。
枚举类

/**
 * 消息队列枚举配置
 *
 * @author Murray
 * @date 2021/5/6 14:30
 */
public enum QueueEnum {
    /**
     * 插件实现延迟队列
     */
    QUEUE_ORDER_PLUGIN_CANCEL("mall.order.direct.plugin", "mall.order.cancel.plugin", "mall.order.cancel.plugin");

    /**
     * 交换名称
     */
    private String exchange;
    /**
     * 队列名称
     */
    private String name;
    /**
     * 路由键
     */
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }

    public String getExchange() {
        return exchange;
    }

    public String getName() {
        return name;
    }

    public String getRouteKey() {
        return routeKey;
    }
}

RabbitMQ配置类

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * RabbitMQ配置类
 *
 * @author Murray
 * @date 2021/5/6 14:24
 */
@Configuration
public class RabbitConfig {

    /**
     * 订单延迟插件消息队列所绑定的交换机
     */
    @Bean
    CustomExchange orderPluginDirect() {
        //创建一个自定义交换机,可以发送延迟消息
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), "x-delayed-message", true, false, args);
    }

    /**
     * 订单延迟插件队列
     */
    @Bean
    public Queue orderPluginQueue() {
        return new Queue(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getName());
    }

    /**
     * 将订单延迟插件队列绑定到交换机
     */
    @Bean
    public Binding orderPluginBinding(CustomExchange orderPluginDirect, Queue orderPluginQueue) {
        return BindingBuilder
                .bind(orderPluginQueue)
                .to(orderPluginDirect)
                .with(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey())
                .noargs();
    }
 	/**
     * 将默认的消息转换器替换成json消息转换器
     */
    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

两个项目都需要订单实体类,且包名也得一样

import java.io.Serializable;
import java.util.UUID;

/**
 * 订单实体类
 *
 * @author Murray Law
 * @date 2021/5/8 14:30
 */
public class Order implements Serializable {
    private static final long serialVersionUID = -8432910728496351007L;
    String orderNumber;
    String userName;
    Integer age;
    String remark;

    public Order() {
    }

    public Order(String userName, Integer age, String remark) {
        this.orderNumber = UUID.randomUUID().toString().trim().replaceAll("-", "");
        this.userName = userName;
        this.age = age;
        this.remark = remark;
    }

    public String getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderNumber='" + orderNumber + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", remark='" + remark + '\'' +
                '}';
    }
}

mq_producer 里创建一个 延时消息队列发出者,通过给消息设置x-delay头来设置消息从交换机发送到队列的延迟时间

import com.murray.mq.producer.config.QueueEnum;
import com.murray.mq.commons.entity.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 订单自动取消的消息发出者
 *
 * @author Murray Law
 * @date  2021/5/6 13:24
 */
@Component
public class CancelOrderSender {
    private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderSender.class);
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendMessage(Order order, final long delayTimes) {
        //给延迟队列发送消息
        amqpTemplate.convertAndSend(QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getExchange(), QueueEnum.QUEUE_ORDER_PLUGIN_CANCEL.getRouteKey(), order, message -> {
            //给消息设置延迟毫秒值
            message.getMessageProperties().setHeader("x-delay", delayTimes);
            return message;
        });
        LOGGER.info("发送出了一个延时取消订单{}", order);
    }
}

mq_producer 里还需要一个订单服务,在订单生成之后调用延时消息发出者的方法

import com.murray.mq.commons.entity.Order;
import com.murray.mq.producer.sender.CancelOrderSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 前台订单管理Service
 *
 * @author Murray Law
 * @date 2021/5/6 13:26
 */

@Service
public class OrderService {

    @Autowired
    private CancelOrderSender cancelOrderSender;

    public void generateOrder(Order order) {
        //TODO 实际项目中会执行一系类下单操作
        //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单
        sendDelayMessageCancelOrder(order);
    }

    private void sendDelayMessageCancelOrder(Order order) {
        //获取订单超时时间,假设为60分钟(测试用的10秒)
        long delayTimes = 10 * 1000;
        //发送延迟消息
        cancelOrderSender.sendMessage(order, delayTimes);
    }
}

既然我们有了消息发出者和订单发出服务,那我们就需要在 mq_consumer 里创建一个延时消息接收者用于处理订单延迟队列中的消息,还有收到订单被取消消息后的订单取消服务

import com.murray.mq.commons.entity.Order;
import com.murray.mq.consumer.service.CancelOrderService;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 订单自动取消的消息接受者
 * 
 * 在spring中,定义rabbitMq的消费者可以相当方便,只需要在消息处理类或者类方法加上@RabbitListener注解,指定队列名称即可。
 * 注解@RabbitListener 可以标注在类上面,需配合 @RabbitHandler 注解一起使用。
 * 当@RabbitListener 标注在类上面表示有收到消息的时候,就交给 @RabbitHandler 的方法处理,具体使用哪个方法处理,根据 MessageConverter 转换后的参数类型。
 * 
 * @author Murray Law
 * @date 2021/5/6 13:23
 */
@Component
@RabbitListener(queues = "mall.order.cancel.plugin")
public class CancelOrderReceiver {
    private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderReceiver.class);
    @Autowired
    private CancelOrderService portalOrderService;

    @RabbitHandler
    public void handle(Order order) {
        LOGGER.info("接受到一个订单因超时未支付{}", order);
        portalOrderService.cancelOrder(order);
    }
}
import com.murray.mq.commons.entity.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 前台订单管理Service
 *
 * @author Murray Law
 * @date 2021/5/6 13:26
 */

@Service
public class CancelOrderService {
    private static Logger LOGGER = LoggerFactory.getLogger(CancelOrderService.class);

    public void cancelOrder(Order order) {
        //TODO 实际项目中执行一系类取消订单操作
        LOGGER.info("已取消订单{}", order);
    }
}

好了,我们启动消息消费端项目 mq_consumer,清空控制台上的启动log,方便我们直观看出新的log记录
启动消费端
清空后清空后

接着我们在 mq_producer 的测试类里写一个测试方法,添加图中红框内代码。
测试类
运行测试类后 mq_producer 的控制台,注意时间 1:03:57orderNumber
测试类运行效果

等待10秒后, mq_consumer 的控制台,消息时间是 1:04:07
因为网络等原因额外延迟了 264ms
消费者时间
好了,一个简单的RabbitMQ延迟队列就这么完成啦, 撒花!!!

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值