RabbitMQ安装与使用
系统环境
1、JDK1.8
2、Centos7-64位
3、Erlang-OTP 23
4、RabbitMQ-3.8.5
linux版本信息查看
cat /proc/version
Linux version 3.10.0-1127.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) ) #1 SMP Tue Mar 31 23:36:51 UTC 2020
uname -r
3.10.0-1127.el7.x86_64
cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)
uname -s
Linux
cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
安装Erlang
erlang和rabbitmq的版本对应关系
通过rpm安装erlang
完成erlang的前置条件配置
curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash
安装erlang
yum install -y erlang
检查erlang的版本号
erl
安装RabbitMQ
先导入两个key
rpm --import https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey
rpm --import https://packagecloud.io/gpg.key
没报错就表示正常
完成RabbitMQ的前置条件配置
curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash
下载RabbitMQ安装包
注意看CentOS的版本,6,7,8都有。我这里是7.4。有时候直接点击浏览器下载可能会很慢,可以F12,找到链接,在centos里面去使用wget下载,可能会很快。这里给出Centos7和Centos8的下载链接。
CentOS7:
https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.5/rabbitmq-server-3.8.5-1.el7.noarch.rpm
CentOS8:
https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.5/rabbitmq-server-3.8.5-1.el8.noarch.rpm
下载成功后,上传到服务器,然后使用命名安装。
rpm -ivh rabbitmq-server-3.8.5-1.el7.noarch.rpm
仔细看有一个警告和一个错误。警告是缺少key,而错误是socat,只需要导入key和安装socat即可。
解决:
- 导入key
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc
- 安装socat
yum -y install epel-release
yum -y install socat
- 再次安装RabbitMQ
rpm -ivh rabbitmq-server-3.8.5-1.el7.noarch.rpm
启用管理平台插件,启用插件后,可以可视化管理RabbitMQ。
rabbitmq-plugins enable rabbitmq_management
启动RabbitMQ
systemctl start rabbitmq-server
访问控制台界面
访问地址
192.168.10.88是安装RabbitMQ的服务器IP地址。如果访问不了,确认是否开启端口5672和15672。
用户登录
RabbitMQ3.3以后,guest账号只能在本机登录。这里就不去修改相应配置了,而是另外创建其他登录账号。
创建用户
创建用户名admin,密码abcd的用户:
rabbitmqctl add_user admin abcd
设置admin为超级管理员
rabbitmqctl set_user_tags admin administrator
授权远程访问(也可以登录后,可视化配置)
rabbitmqctl set_permissions -p / admin "." "." ".*"
创建完成后,重启RabbitMQ
systemctl restart rabbitmq-server
登录用户admin
设置虚拟主机
在正常安装RabbitMQ之后, 系统会默认创建一个虚拟主机 名称以 / 的形式,看到admin是No access的,需要设置一下虚拟主机
点击Virtual Hosts 添加一个名为test的虚拟主机
添加成功后点击test这里我们为虚拟主机添加用户
点击权限后, 选择User的下拉中就能看到我们的刚添加的用户
点击Set permission 设置权限后就能在上方权限列表中看见我们刚添加的用户
回到虚拟主机的页面后, 我们发现会多出一条记录
页面配置
添加队列 Queues
创建交换机 Exchanges
选择交换机 Exchanges 加入已经创建好的队列 Queues
延迟队列
简介
延迟队列,名字中有个队列,队列是先进先出的。所以说延迟队列是一个有方向性的。
其次,延迟队列和普通队列最大的区别就是,普通队列里的消息是希望自己早点被取出来消费。而延迟队列中的消息都是由时间来控制的。也就是说,他们进入队列的时候,就已经被安排何时被取出了
rabbitmq实现延迟队列主要有种方式。
-
第一种是使用普通队列和死信队列来模拟实现延迟的效果。大致上是将消息放入一个没有被监听的队列上,设置TTL(一条消息的最大存活时间)为延迟的时间,时间到了没有被消费,直接成为私信。监听私信队列来进行操作。
-
第二种是使用rabbitmq官方提供的delayed插件来真正实现延迟队列。本文对第二种进行详解
应用场景
订单超时支付取消订单
用户发起退款卖家3天不处理自动退款
预约抢购活动,活动开始前10分钟短信通知用户
安装延迟插件
默认交换机是有4种模式的
下载延迟插件: https://www.rabbitmq.com/community-plugins.html
下载完成之后,上传到服务器mq容器的plugins目录下
安装插件:rabbitmq-plugins enable rabbitmq_delayed_message_exchange
交换机新增一个延迟模式
配置
配置类
初始化一个队列和一个延迟交换机(这里我交换机模式用的是路由模式)。将队列绑定到交换机上并设置路由Key
@Component
public class RabbitmqDelayedConfig {
/**
* 初始化延迟交换机
* @return
*/
@Bean
public CustomExchange delayedExchangeInit() {
Map<String, Object> args = new HashMap<>();
// 设置类型,可以为fanout、direct、topic
args.put("x-delayed-type", "direct");
// 第一个参数是延迟交换机名字,第二个是交换机类型,第三个设置持久化,第四个设置自动删除,第五个放参数
return new CustomExchange("delayed_exchange","x-delayed-message", true,false,args);
}
/**
* 初始化短信队列
* @return
*/
@Bean
public Queue delayedSmsQueueInit() {
return new Queue("sms_delayed_queue", true);
}
/**
* 短信队列绑定到交换机
* @param delayedSmsQueueInit
* @param customExchange
* @return
*/
@Bean
public Binding delayedBindingSmsQueue(Queue delayedSmsQueueInit, CustomExchange customExchange) {
// 延迟队列绑定延迟交换机并设置RoutingKey为sms
return BindingBuilder.bind(delayedSmsQueueInit).to(customExchange).with("sms").noargs();
}
}
生产者
将消息转发到"delayed_exchange"交换机上路由键为"sms"的队列中
@Component
@Log4j2
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 延迟模式
* @param msg 消息
* @param time 延迟时间
*/
public void delayedTest(String msg,Integer time) {
// 第一个参数是延迟交换机名称,第二个是Routingkey,第三个是消息主题,第四个是X,并设置延迟时间,单位 是毫秒
rabbitTemplate.convertAndSend("delayed_exchange","sms",msg,a -> {
a.getMessageProperties().setDelay(time);
return a;
});
log.info("延迟模式默认交换机已发出消息");
}
}
消费者
监听指定的队列。一旦队列中有消息则立刻取出进行消费
@Component
@Log4j2
public class ConsumerDelayed {
/**
* 延迟模式短信消费者
* @param message
*/
@RabbitListener(queues = {"sms_delayed_queue"})
public void getSmsConsumer(String message) {
log.info(new Date().toLocaleString() + "延迟模式短信消费者接收到信息:" + message);
}
}
发出消息并且10S后延迟队列对消息进行消费
@RabbitListener
在spring中,定义rabbitMq的消费者可以相当方便,只需要在消息处理类或者类方法加上@RabbitListener
注解,指定队列名称即可。如下代码:
@Component
public class RabbitMqListener1 {
@RabbitListener(queues = "queue1")
public void consumer1(Message message) {
}
@RabbitListener(queues = "queue2")
public void consumer2(String messsageBody) {
}
}
@Component
@RabbitListener(queues = "queue3")
public class RabbitMqListener2 {
@RabbitHandler(isDefault=true)
public void consumer3() {
}
}
RabbitMQ 图形化说明:
https://www.cnblogs.com/chenshaojun2008/p/17250864.html
springboot集成RabbitMQ
- 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 配置RabbitMQ连接
在application.properties
或application.yml
中配置RabbitMQ服务器的连接参数:
# 总开关配置
spring.rabbitmq.enable=true
# 定义RabbitMQ的主机地址,这里使用的是局域网内的一个IP地址
spring.rabbitmq.host=192.168.131.130
# 指定RabbitMQ的端口号,默认情况下RabbitMQ使用5672端口
spring.rabbitmq.port=5672
# 设置RabbitMQ的用户名,这里使用的是默认的用户名guest
spring.rabbitmq.username=guest
# 设置RabbitMQ的密码,这里使用的是默认的密码guest
spring.rabbitmq.password=guest
# 配置RabbitMQ的虚拟主机,这里使用的是默认的虚拟主机"/"
spring.rabbitmq.virtual-host=/
@ConditionalOnProperty(name = "spring.rabbitmq.enable", havingValue = "true")
当spring.rabbitmq.enabled
的值为true
时,RabbitMqConfig配置类中的Bean会被创建,否则不会创建。这样你就可以根据需要开启或关闭RabbitMQ的配置。
- 使用
一个交换机绑定一个队列
- 消费者
@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.enable", havingValue = "true")
public class DirectConsumer extends RabbitAutoConfiguration {
//注册一个队列
@Bean //启动多次为什么不报错?启动的时候,它会根据这个名称Direct_Q01先去查找有没有这个队列,如果有什么都不做,如果没有创建一个新的
public Queue directQueue(){
return QueueBuilder.durable("Direct_Q01").maxLength(100).build();
}
//注册交换机
@Bean
public DirectExchange directExchange(){
//1.启动的时候,它会根据这个名称Direct_E01先去查找有没有这个交换机,如果有什么都不做,如果没有创建一个新的
return ExchangeBuilder.directExchange("Direct_E01").build();
}
//绑定交换机与队列关系
@Bean
public Binding directBinding(Queue directQueue,DirectExchange directExchange){
return BindingBuilder.bind(directQueue).to(directExchange).with("RK01");
}
//启动一个消费者
@RabbitListener(queues = "Direct_Q01")
public void receiveMessage(String msg){
System.out.println("Direct_Q01收到消息:"+msg);
}
}
- 生产者
//放入Ioc容器
@Service
public class DirectProvider {
@Resource
private RabbitTemplate rabbitTemplate;
//发送消息
public void send(String message) {
rabbitTemplate.convertAndSend("Direct_E01", "RK01", message);
}
}
- 测试
@SpringBootTest(classes = App.class)
public class TestDirect {
@Resource
private DirectProvider directProvider;
@Test
public void directSendTest(){
for (int i = 0; i < 10; i++) {
directProvider.send("我嫩爹");
}
}
}
一个交换机对多个队列
- 消费者
@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.enable", havingValue = "true")
public class FanoutConsumer extends RabbitAutoConfiguration {
//注册一个队列
@Bean
public Queue fanoutQueue(){
return QueueBuilder.durable("Fanout_Q01").maxLength(100).build();
}
@Bean
public Queue fanoutQueue2(){
return QueueBuilder.durable("Fanout_Q02").maxLength(100).build();
}
//注册交换机
@Bean
public FanoutExchange fanoutExchange(){
return ExchangeBuilder.fanoutExchange("Fanout_E01").build();
}
//绑定交换机与队列关系
@Bean
public Binding fanoutBinding(Queue fanoutQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
}
@Bean
public Binding fanoutBinding2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
//启动一个消费者
@RabbitListener(queues = "Fanout_Q01")
public void receiveMessage(String msg){
System.out.println("Fanout_Q01收到消息:"+msg);
}
//启动一个消费者
@RabbitListener(queues = "Fanout_Q02")
public void receiveMessage2(String msg){
System.out.println("Fanout_Q02收到消息:"+msg);
}
}
- 生产者
@Service
public class FanoutProvider {
@Resource
private RabbitTemplate rabbitTemplate;
public void send(JSONObject message) {
rabbitTemplate.convertAndSend("Fanout_E01","",message.get("msg"));
}
}
- 测试
@RestController
@RequestMapping("/fanout")
public class FanoutController {
@Resource
private FanoutProvider fanoutProvider;
@PostMapping("/send")
public void send(@RequestBody JSONObject message) {
fanoutProvider.send(message);
}
}
一个队列对多个消费者
- 消费者
@Configuration
@ConditionalOnProperty(name = "spring.rabbitmq.enable", havingValue = "true")
public class TopicConsumer extends RabbitAutoConfiguration {
//注册一个队列
@Bean
public Queue topicQueue(){
return QueueBuilder.durable("Topic_Q01").maxLength(100).build();
}
//注册交换机
@Bean
public TopicExchange topicExchange(){
return ExchangeBuilder.topicExchange("Topic_E01").build();
}
//绑定交换机与队列关系
@Bean
public Binding topicBinding(Queue topicQueue,TopicExchange topicExchange){
return BindingBuilder.bind(topicQueue).to(topicExchange).with("#");
}
//启动一个消费者
@RabbitListener(queues = "Topic_Q01")
public void receiveMessage(String msg){
System.out.println("Topic_Q01收到消息:"+msg);
}
//启动一个消费者
@RabbitListener(queues = "Topic_Q01")
public void receiveMessage2(String msg){
System.out.println("Topic_Q02收到消息:"+msg);
}
}
- 生产者
@@Service
public class TopicProvider {
@Resource
private RabbitTemplate rabbitTemplate;
public void send(JSONObject message) {
rabbitTemplate.convertAndSend("Topic_E01",message.get("routingKey").toString(),message.get("msg"));
}
}
- 测试
@RestController
@RequestMapping("/fanout")
public class FanoutController {
@Resource
private TopicProvider topicProvider ;
@PostMapping("/send")
public void send(@RequestBody JSONObject message) {
topicProvider.send(message);
}
}
原文链接:
https://blog.csdn.net/weixin_40584261/article/details/106826044
https://blog.csdn.net/qq_28967139/article/details/119216362
https://blog.csdn.net/qq_40179653/article/details/125618655
https://blog.csdn.net/qq_57036151/article/details/141174303