RabbitMQ是一款基于AMQP协议(高级消息队列网络协议)标准、采用Erlang语言开发的消息中间件,基于AMQP协议的客户端与中间件传递消息不受两端的不同产品不同开发语言的条件限制。
消息进入队列后不会立即被消费,只有到达指定时间后才会被消费;
RabbitMQ默认不支持延迟队列
实现方式:定时器、延迟队列(TTL+死信队列)
应用场景:商品下单后30min未支付则取消订单回滚交易、用户注册7days后短信问候等
- 队列消息长度超过限制
- 消费者不消费消息,并且不重回队列
- 消息到达超时时间未消费
避免消息被重复消费,需要实现消息幂等性保障方案:
使用工作队列模式,增加消息接收者和相应队列消费消息,将消息先批量取出来记录数据库,然后慢慢处理
fanout发布订阅模式
一个消息发送者可能对应多个消息接收者;
消息从发送者发到Broker后首先进入到交换机(X)中,X根据分发规则匹配查询表中的路由key将消息分发给bingding的队列,消息接收者监听对应的队列消费消息(交换机的类型主要有四种:direct, fanout, topic, headers)
direct路由模式
路由模式是以指定的完整路由key将消息发送者发送的消息发送给指定消息接收者;
消息发送者携带指定路由key将消息发送到direct交换机,然后通过交换机的路由匹配规则转发到binding的队列中,消息接收者监听指定队列消费消息
topic主题模式(通配符模式)
topic模式即通配符匹配路由key,通配符包含“*”和“#”,星号代表匹配一个单词,而#代表匹配零个、一个或多个单词;
消息发送者携带指定路由key将消息发送到topic交换机,然后通过交换机的通配符匹配规则转发到binding的队列中,消息接收者监听指定队列消费消息
安装环境:CentOS-7.*
进入/usr/local目录
cd /usr/local
yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz
rz 选择要上传的文件
安装erlang
rpm -ivh erlang-22.0.7-1.el7.x86_64.rpm
yum install -y socat
rpm -ivh rabbitmq-server-3.7.18-1.el7.noarch.rpm
修改默认配置,如可以修改密码,修改loopback_users等配置
vi /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18/ebin/rabbit.app
修改rabbit.app配置文件中loopback_users,将其值由[<<“guest”>>]改为[guest]后保存并退出即可(注:此操作用于设置浏览器访问rabbitmq控制台界面的用户名)
:wq
service rabbitmq-server start (启用服务)
service rabbitmq-server stop(停止服务)
service rabbitmq-server restart(重启服务)
service rabbitmq-server status(查看服务状态)
rabbitmq-server服务启动后,浏览器访问 http://192.168.126.133:15672,输入用户名密码(都是guest)即可进入控制台界面
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
server:
port: 8080
#rabbitmq singleton config
spring:
application:
name: rabbitmq-demo
rabbitmq:
host: 192.168.126.134
port: 5672
username: jeffrey
password: zfy
virtual-host: jeffrey_host
topicExchange: jeffrey_topic_exchange
topicQueue1: jeffrey_topic_queue1
topicQueue2: jeffrey_topic_queue2
package com.itjeffrey.rabbitmq.test.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* topic模式配置(有路由key, 模糊匹配,有交换机)
* @From: Jeffrey
* @Date: 2022/11/19
*/
@Configuration
public class TopicConfig {
@Value("${spring.rabbitmq.topicExchange}")
private String exchange;
@Value("${spring.rabbitmq.topicQueue1}")
private String queue1;
@Value("${spring.rabbitmq.topicQueue2}")
private String queue2;
//声明交换机
@Bean
public Exchange topicExchange(){
return ExchangeBuilder.topicExchange(exchange).durable(false).build();
}
//声明队列
@Bean
public Queue topicQueue1(){
return QueueBuilder.nonDurable(queue1).build();
}
@Bean
public Queue topicQueue2(){
return QueueBuilder.nonDurable(queue2).build();
}
//队列绑定交换机,并指定路由key
@Bean
public Binding topicBinding1(){
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("TR.*").noargs();
}
@Bean
public Binding topicBinding2(){
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("TR.#").noargs();
}
}
package com.itjeffrey.rabbitmq.test.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
/**
* @From: Jeffrey
* @Date: 2022/11/19
*/
@Service
public class Producer {
@Value("${spring.rabbitmq.topicExchange}")
private String topicExchange;
@Autowired
private RabbitTemplate rabbitTemplate;
//topic模式
@Scheduled(cron = "0/10 * * * * ?")
public void send5(){
String msg = "test message";
rabbitTemplate.send(topicExchange, "TR.A.B",
new Message(msg.getBytes(StandardCharsets.UTF_8), new MessageProperties()));
System.out.println("send topic msg!");
}
}
package com.itjeffrey.rabbitmq.test.service;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @From: Jeffrey
* @Date: 2022/11/19
*/
@Service
public class Consumer {
@RabbitListener(queues = {"jeffrey_topic_queue1"})
public void listen1(Message message){
System.out.println("receive topic_queue1 msg -> " + new String(message.getBody()));
}
@RabbitListener(queues = {"jeffrey_topic_queue2"})
public void listen2(Message message){
System.out.println("receive topic_queue2 msg -> " + new String(message.getBody()));
}
}
企业集群部署一般采用一个HAProxy负载均衡器代理和两个RabbitMQ-Server服务分别部署在三个不同服务器节点的方式;也可以采用HAProxy+KeepAlived一主一备作为负载均衡代理集群和三台RabbitMQ-Server服务器集群的方式增强MQ的高可用性
企业级分布式集群部署步骤如下:
hostnamectl set-hostname s1
hostnamectl set-hostname s2
#s2可以替换为ip
scp /var/lib/rabbitmq/.erlang.cookie s2:/var/lib/rabbitmq/.erlang.cookie
#stop_app禁止rabbitmq接收外部请求
rabbitmqctl stop_app
rabbitmqctl join_cluster --ram rabbit@s2
#start_app允许rabbitmq接收外部请求
rabbitmqctl start_app
rabbitmq-plugins enable rabbitmq_management
systemctl restart rabbitmq-server.service
rabbitmqctl cluster_status
#安装haproxy
yum install haproxy
#修改haproxy.cfg配置文件
vi /etc/haproxy/haproxy.cfg
#添加配置
#监听rabbitmq集群
listen rabbitmq_cluster
#通过端口5672映射s1,s2
bind 0.0.0.0:5672
#记录tcp连接状态和连接时间
option tcplog
#四层协议代理,对tcp协议转发
mode tcp
#开启tcp的keep alive长连接模式
option clitcpka
#haproxy与rabbitmq-server的连接超时时间
timeout connect 1s
#client端与haproxy最大空闲时间
timeout client 10s
#server端与haproxy最大空闲时间
timeout server 10s
#轮询转发消息
balance roundrobin
#每5s发一次心跳包,连续两次有响应则表示连接状态良好,连续三次无响应则表示服务故障该节点将被踢除
server node1 192.168.126.134:5672 check inter 5s rise 2 fall 3
server node2 192.168.126.133:5672 check inter 5s rise 2 fall 3
#开启haproxy前端监控服务
listen http_front
#绑定监控服务端口1080
bind 0.0.0.0:1080
#每30s刷新一次监控统计页面
stats refresh 30s
#指定监控统计页面uri
stats uri /haproxy_stats
#指定监控统计页面的访问用户名和密码
stats auth admin:admin
:wq
#启动haproxy
systemctl start haproxy
#查看haproxy状态
systemctl status haproxy.service
http://haproxy服务器IP:1080/haproxy_stats
(注:本篇文章部分图例引用自网络)