笔者在安装RabbitMQ时发现大部分博客记录的安装复杂且版本固定且过时,或复制粘贴缺胳膊少腿;故写下此博客,希望有需要的同学可以少走些弯路。
笔者设备: win10+Linux CentOS7。
笔者RabbitMQ版本3.8.16,Java版本11,Spring Boot版本2.4.5。
使用win10系统去连接服务器不需要安装其它任何软件。
服务器安装使用root用户,使用rpm包。
本文宗旨操作简单化,描述详细化 。
项目源码:https://github.com/MuweiLaw/RabbitMQ.git
项目源码分为两个服务,一个生产者一个消费者,适用于分布式架构。
进入 RabbitMQ下载地址,下载RabbitMQ的包。
笔者的Linux版本均为CentOS7,故下载图中蓝框版本
蓝色框上方为CentOS8版本
点击下载放入到我们刚才建好的文件夹中
进入延迟插件下载地址,在页面使用Ctrl+F直接搜索rabbitmq_delayed_message_exchange即可找到我们需要下载的插件
下载和RabbitMQ配套的版本
RabbitMQ是基于Erlang语言开发,so在安装RabbitMQ之前,需要先安装Erlang,这里使用RabbitM
Q团队提供的Erlang依赖包。
进入Erlang下载页
点击下载放入到我们刚才建好的文件夹中
回到电脑桌面
使用快捷键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连接
IP查看方式:
1.虚拟机: 使用ifconfig命令
2.云服务器:在云服务器控制台上找到服务器的公网IP,笔者是一台最低配阿里云ECS服务器,年费用不超过100软妹币,满足日常学习开发使用,其它高配云服务器享受同等折扣,链接戳我直达
使用Xshell,PuTTY,CRT等ssh工具的可以跳过这一步
打开windows运行命令行
输入ssh user@host这里user依旧是你的Linux账户名,host依旧是你的Linux系统对外的IP地址。密码不显示,输入密码时尽量不要使用小键盘。
图示我们已经通过ssh用root账号连接上了我们的服务器,接下来我们将正式在服务器搭建环境
依次执行如下命令:
rpm -ivh erlang-23.3.2-1.el7.x86_64.rpm 安装erlang依赖
yum -y install socat 安装socat
rpm -ivh rabbitmq-server-3.8.16-1.el7.noarch.rpm 安装RabbitMQ
接下来我们启动RabbitMQ
使用命令 systemctl start rabbitmq-server 稍等几秒钟,正常启动如下,没有任何提示
systemctl enable rabbitmq-server 设置RabbitMQ开机自启
rabbitmq-plugins enable rabbitmq_management 启用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控制台输入账号密码后会发现如下图所示
所以我们还需要让guest用户能够远程访问
cd /etc/rabbitmq 进入到 etc/rabbitmq 目录下
touch rabbitmq.config 创建名为 rabbitmq.config 的配置文件
vi rabbitmq.config 编辑配置文件
按下回车键之后效果如下:注意红框内描述变化
此时我们按下键盘 “ i ” 键,进行编辑,注意左下角变化,表示已进入编辑状态
输入 [{rabbit, [{loopback_users, []}]}].
再按下键盘“ Esc ”键退出编辑模式键盘依次按下 :wq
再按下回车键后保存并退出,注意此处冒号是英文冒号
systemctl restart rabbitmq-server重启RabbitMQ,等待数秒
再次访问web控制台
安装延迟插件
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-message
环境搭建大功告成!!!
项目源码:https://github.com/MuweiLaw/RabbitMQ.git
笔者用的IDE是intellij IDEA,新建两个Spring Initializr项目
项目1:mq_producer
项目2:mq_consumer
首先我们需要在两个项目中的pom.xml文件中都添加这3个依赖;
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jsonartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
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;
/**
* 订单自动取消的消息接受者
*
* @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:57 和 orderNumber
等待10秒后, mq_consumer 的控制台,消息时间是 1:04:07
因为网络等原因额外延迟了 264ms
好了,一个简单的RabbitMQ延迟队列就这么完成啦, 撒花!!!