rabbitmq消息中间件读后感
四.RabbitMQ安装与使用
1.安装(rpm方式)
下载对应版本的Erlang和RabbitMQ。
安装erlang:
安装socat密钥:
要先安装socat再安装rabbitmq,不然会报错。
安装rabbitmq:
2.修改配置文件
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
默认端口号5672
修改loopback_users:(保留guest也可)
3.RabbitMq基本命令
关于服务的操作:
服务启动:rabbitmqctl start_app / rabbitmq-server start &
这里有用到主机名,可以通过vim /etc/hostname来配置。
可以看到rabbitmq已经启动:
查看服务是否启动 lsof -i:5672
服务停止:rabbitmqctl stop_app / rabbitmq-server stop
服务重启:service rabbitmq-server restart
节点状态:rabbitmqctl status
启动管理控制台:
管理插件:rabbitmq-plugins enable rabbitmq_management
控制台访问地址:http://192.168.58.129:15672
如果既修改过配置文件,又开启了插件,但是还访问失败,可以看下是不是没有关闭防火墙。
firewalld的基本使用
启动: systemctl start firewalld
关闭: systemctl stop firewalld
查看状态: systemctl status firewalld
开机禁用: systemctl disable firewalld
开机启用: systemctl enable firewalld
关于用户的操作:
添加用户:rabbitmqctl add_user username password
列出所有用户:rabbitmqctl list_users
删除用户:rabbitmqctl delete_user username
清除用户权限:rabbitmqctl clear_permissions -p vhostpath username
列出用户权限:rabbitmqctl list_user_permissions username
修改密码:rabbitmqctl change_password username newpassword
设置用户权限:rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*"
关于虚拟主机的操作:
创建虚拟主机:rabbitmqctl add_vhost vhostpath
列出所有虚拟主机:rabbitmqctl list_vhost
列出虚拟主机上所有权限:rabbitmqctl list_permissions -p vhostpath
删除虚拟主机:rabbitmqctl delete_vhost vhostpath
关于消息队列的操作:
查看所有队列信息:rabbitmqctl list_queues
清除队列里的消息:rabbitmqctl -p vhostpath purge_queue blue
等等。
4.RabbitMq高级命令
rabbitmqctl reset:移除所有数据,要在rabbitmqctl stop_app之后使用。
组成集群命令:rabbitmqctl join_cluster <clusternode> [--ram] (ram内存级别存储,disc磁盘)
查看集群状态:rabbitmqctl cluster_status
修改集群节点的存储形式:rabbitmqctl change_cluster_node_type disc | ram
忘记(摘除)节点:rabbitmqctl forget_cluster_node [--offline] (offline服务不启动的情况下)
修改节点名称:rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2] [newnode2 ...]
1:RabbitMQ是一个开源的消息代理和队列服务器,可以通过基本协议在完全不同的应用之间共享数据,使用Erlang语言开发的,是基于AMQP(高级消息队列协议)协议,Erlang主要用于交换机的开发,有着与原生socket一样的延迟这也是为什么RabbitMQ高性能的原因
2:AMQP(高级消息队列协议)协议模型
3:核心概念
server:又称Broker
Connection:连接,应用程序和server的连接
channel:网络信道,几乎所有的操作都在channel上进行的,channel是进行消息读写的通道,每个客户端可以建立多个channel,
每个channel代表一个会话任务
message:有Properties和body组成,Properties可以对消息进行修饰如消息的优先级,消息的延迟,body就是消息的内容
Virtual host:虚拟地址,用于进行逻辑隔离,最上面的消息路由,一个Virtual host可以有若干个exchange和queue,但是一个Virtual host不能有相同的exchange和queue
exchange:交换机接受消息,根据路由键转发到绑定的队列
binding:exchange和queue之间的虚拟连接;binding 可以包含routing key
routing key:路由规则
4:依赖项目
- <dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
5:生产者代码
- public class Procuder {
public static void main(String[] args) throws Exception {
//1 创建一个ConnectionFactory, 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3 通过connection创建一个Channel
Channel channel = connection.createChannel();
//4 通过Channel发送数据
for (int i = 0; i < 5; i++) {
String msg = "Hello RabbitMQ!";
//1 exchange 2 routingKey
//props是消息的一些属性设置
//当exchange为空时候会路由到和routingkey一样名字的消息队列
channel.basicPublish("", "test001", null, msg.getBytes());
}
//5 记得要关闭相关的连接
channel.close();
connection.close();
}
}
6:消费者
- public class Consumer {
public static void main(String[] args) throws Exception {
//1 创建一个ConnectionFactory, 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3 通过connection创建一个Channel
Channel channel = connection.createChannel();
//4 声明(创建)一个队列
String queueName = "test001";
//durable代表声明的队列持久化到服务器上
//exclusive是否为当前连接的专用队列,在连接断开后,会自动删除该队列
//autoDelete 当没有任何消费者使用时,自动删除该队列
//arguments 其他队列配置
channel.queueDeclare(queueName, true, false, false, null);
//5 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6 设置Channel
//autoAck
//自动确认
//指定autoAck参数,当autoAck=true时,一旦消费者接收到了消息,就视为自动确认了消息。如果消费者在处理消息的过程中,中间出错就无法重新处理该消息,所以需要经常性的在代码里进行手动确认。
//
//手动确认
//需设置autoAck=false,此时RabbitMQ会等待消费者显式发回ACK信号后才从内存(或磁盘)中移去消息,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为RabbitMQ会一直持有消息直到消费者显式调用basicAck为止。(消息此处会有两份,投递出去的和broke自留的,如果投递未收到Ack且连接断开,broker将会把消息投递给下一个消费者)
//一般使用手动确认会将消息的处理放在try/catch语句块中,成功处理了,就给MQ一个确认应答,如果处理异常了,就在catch中,进行消息的拒绝。
channel.basicConsume(queueName, true, queueingConsumer);
while (true) {
//7 获取消息
Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.err.println("消费端: " + msg);
//Envelope envelope = delivery.getEnvelope();
}
}
}
7:direct exchange consumer
- public class Consumer4DirectExchange {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//当出现网络抖动的时候尝试重新连接
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_direct_exchange";
String exchangeType = "direct";
String queueName = "test_direct_queue";
String routingKey = "test.direct";
//表示声明了一个交换机
//internal 标识的是是否是内部使用 通常设置为false即可
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
//表示声明了一个队列
channel.queueDeclare(queueName, false, false, false, null);
//建立一个绑定关系:
channel.queueBind(queueName, exchangeName, routingKey);
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
8:direct exchange product
- public class Producer4DirectExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_direct_exchange";
String routingKey = "test.direct111";
//5 发送
String msg = "Hello World RabbitMQ 4 Direct Exchange Message 111 ... ";
channel.basicPublish(exchangeName, routingKey , null , msg.getBytes());
}
}
9:所有发送到Direct Exchange的消息被转发到RouteKey中指定的Queue。
Direct模式,可以使用rabbitMQ自带的Exchange:default Exchange 。所以不需要将Exchange进行任何绑定(binding)操作 。消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃。
10:topic exchange
一个exchange可以有两个不同的routekey 绑定同一个队列
routingkey模糊匹配
- consumer
- public class Consumer4TopicExchange {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String exchangeType = "topic";
String queueName = "test_topic_queue";
//String routingKey = "user.*";匹配一个词
//String routingKey = "user.#";匹配多个词
String routingKey = "user.*";
// 1 声明交换机
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
// 2 声明队列
channel.queueDeclare(queueName, false, false, false, null);
// 3 建立交换机和队列的绑定关系:
channel.queueBind(queueName, exchangeName, routingKey);
//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
product
- public class Producer4TopicExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String routingKey1 = "user.save";
String routingKey2 = "user.update";
String routingKey3 = "user.delete.abc";
//5 发送
String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes());
channel.close();
connection.close();
}
}
- 11:fanout exchange
不需要要routingkey 路由 发送到所有exchange绑定的队列上 所以性能最高
- Producer
- public class Producer4FanoutExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_fanout_exchange";
//5 发送
for(int i = 0; i < 10; i ++) {
String msg = "Hello World RabbitMQ 4 FANOUT Exchange Message ...";
channel.basicPublish(exchangeName, "", null , msg.getBytes());
}
channel.close();
connection.close();
}
}
- Consumer
- public class Consumer4FanoutExchange {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_fanout_exchange";
String exchangeType = "fanout";
String queueName = "test_fanout_queue";
String routingKey = ""; //不设置路由键
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
channel.queueDeclare(queueName, false, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
12:message 相关属性
product
- Map<String, Object> headers = new HashMap<>();
headers.put("my1", "111");
headers.put("my2", "222");
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)//是否持久化
.contentEncoding("UTF-8")
.expiration("10000")//消息过期时间 当过期了会自动抛弃
.headers(headers)//自定义消息属性
.build();- //4 通过Channel发送数据
for(int i=0; i < 5; i++){
String msg = "Hello RabbitMQ!";
//1 exchange 2 routingKey
channel.basicPublish("", "test001", properties, msg.getBytes());
}
consumer
- while(true){
//7 获取消息
Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.err.println("消费端: " + msg);
Map<String, Object> headers = delivery.getProperties().getHeaders();
System.err.println("headers get my1 value: " + headers.get("my1"));- //Envelope envelope = delivery.getEnvelope();
}
13:生产端消息可靠性投递
(1)保证消息的成功发出(2)保证mq节点的成功接收(3)发送端收到mq节点的确认应答(4)完善的消息补偿机制
通常有两种方式
(1)消息落库,对消息状态进行打标
(2)消息的延迟投递,做二次确认,回调检查
消息落库方案如下
消息的延迟投递方案如下
14:消息重复投递的幂等性问题
(1)通过唯一ID利用数据库主键去重(高并发情况下通过分库分表来提高性能)
(2)利用redis原子性去实现()
15:confirm确认机制
生产者投递消息后如果broker收到消息就会给生产端一个应答,生产端接收应答用来确认这个消息是否被正常发送到broker这就是消息可靠性投递的核心需要以下两个步骤
(1)第一步需要在channel上开启确认模式
- //4 指定我们的消息投递模式: 消息的确认模式
channel.confirmSelect();
(2)在channel上添加监听addConfirmListener监听成功和失败的返回结果
- //6 添加一个确认监听
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.err.println("-------no ack!-----------");
}- @Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.err.println("-------ack!-----------");
}
});
完整代码如下
- public class Producer {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 获取C onnection
Connection connection = connectionFactory.newConnection();
//3 通过Connection创建一个新的Channel
Channel channel = connection.createChannel();
//4 指定我们的消息投递模式: 消息的确认模式
channel.confirmSelect();
String exchangeName = "test_confirm_exchange";
String routingKey = "confirm.save";
//5 发送一条消息
String msg = "Hello RabbitMQ Send confirm message!";
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
//6 添加一个确认监听
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.err.println("-------no ack!-----------");
//可能是最大队列已满,磁盘不足等状况下触发
}
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.err.println("-------ack!-----------");
}
});
}
}
16:Return 消息机制
Return Listener 用于处理监听一些不可路由的消息(exchange不存在,或者routing key 路由不到)
- mandatory为true,监听器就会接收路由不可达的消息然后做进一步处理
- mandatory为false 那么broker会自动删除消息
代码如下:
- public class Producer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String exchange = "test_return_exchange";
String routingKey = "return.save";
String routingKeyError = "abc.save";
String msg = "Hello RabbitMQ Return Message";
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange,
String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {- System.err.println("---------handle return----------");
System.err.println("replyCode: " + replyCode);
System.err.println("replyText: " + replyText);
System.err.println("exchange: " + exchange);
System.err.println("routingKey: " + routingKey);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
}
});
//mandatory 为true时监听器就会接收路由不可达的消息然后做进一步处理,false 会自动删除
- channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
//channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
}
}
17:消费端自定义监听
我们通过while循环进行获取消息进行消费处理,我们也可以自定义consumer更加方便解耦性更加强
public class Consumer {
- public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String exchangeName = "test_consumer_exchange";
String routingKey = "consumer.#";
String queueName = "test_consumer_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
- MyConsumer代码如下
- public class MyConsumer extends DefaultConsumer {
- public MyConsumer(Channel channel) {
super(channel);
}- @Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
//同一个会话, consumerTag 是固定的 可以做此会话的名字, deliveryTag 每次接收消息+1,可以做此消息处理通道的名字。
//因此 deliveryTag 可以用来回传告诉 rabbitmq 这个消息处理成功 清除此消息(basicAck方法)
- }
- }
- 19:消费端限流
使用场景:巨量消息瞬间全部推送过来,单个客户端无法处理这么多消息
在非自动确认消息的前提下。通过设置consumer或者channel设置的qos的值,如果一定数量的消息未被确认就不进行消息的消息
- //1 限流方式 第一件事就是 autoAck设置为 false
- channel.basicQos(0, 1, false);
- channel.basicConsume(queueName, false, new MyConsumer(channel));
- basicQos(int prefetchSize, int prefetchCount, boolean global)
- //global是否将以上设置应用于channel还是consumer级别。
- prefetchSize 和global这两个配置rabbitmq暂时还没实现
- rabbitmq向第一个消费者投递了prefetchCount条消息后,消费者未对prefetchCount条消息进行ack,rabbitmq不会再向该消费者投递消息
- 手动签收
- @Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));- channel.basicAck(envelope.getDeliveryTag(), false);
- }
- 20:消费端ack和nack
手工签收与重回队列 (一般实际工作中不会重回队列)
- 消息消费者手工签收 必须要关闭 autoAck = false
- // 手工签收 必须要关闭 autoAck = false
channel.basicConsume(queueName, false, new MyConsumer(channel));
- MyConsumer代码如下
- public class MyConsumer extends DefaultConsumer {
private Channel channel ;
public MyConsumer(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("body: " + new String(body));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if((Integer)properties.getHeaders().get("num") == 0) {
channel.basicNack(envelope.getDeliveryTag(), false, true);//true 重回队列
} else {
channel.basicAck(envelope.getDeliveryTag(), false);//
}
}
- 21:TTL队列与消息
- 可以在消息上指定消息过期时间,也可以在队列上指定消息过期时间(从消息进入队列开始到指定的超过队列过期时间,
那么消息会自动的清除,在声明队列时可以指定x-message-ttl消息过期时间作为Map<String, Object> arguments的参数)
- AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.headers(headers)
.build();
- 22:死信队列使用
x-max-length 指定队列最大长度
x-message-ttl 指定队列的消息过期时间
Map<String, Object> agruments = new HashMap<String, Object>();
agruments.put("x-dead-letter-exchange", "dlx.exchange");指定当前队列是对应一个exchange为dlx.exchange死信的exchange
- 当消息在一个队列中变成死信的时候,它能重新被publish另一个exchange中
这个exchange就是x-dead-letter-exchange指定的
- public class Consumer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.11.76");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 这就是一个普通的交换机 和 队列 以及路由
String exchangeName = "test_dlx_exchange";
String routingKey = "dlx.#";
String queueName = "test_dlx_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
Map<String, Object> agruments = new HashMap<String, Object>();
agruments.put("x-dead-letter-exchange", "dlx.exchange");
//这个agruments属性,要设置到声明队列上
channel.queueDeclare(queueName, true, false, false, agruments);
channel.queueBind(queueName, exchangeName, routingKey);
//要进行死信队列的声明:
channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
channel.queueDeclare("dlx.queue", true, false, false, null);
channel.queueBind("dlx.queue", "dlx.exchange", "#");
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
消息变成死信的以下几种情况
- 消息被拒绝,并且requeue= false
- 消息ttl过期
- 队列达到最大的长度
- 23:和springboot 整合需要引入以下包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId
</dependency>
- 24:RabbitAdmin类可以很好的操作RabbitMQ,在Spring中直接进行注入即可
(2)autoStartup必须设置true,否则Spring容器不会加载RabbitAdmin类
(3)RabbitAdmin底层实现就是从Spring容器中获取Exchagge,Bingding,RoutingKey以及Queue的@Bean声明
(4)然后使用RabbitTemplate的execute方法执行对应声明,修改,删除等操作
- @Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
- 25:RabbitMQConfig 配置 注入rabbitAdmin
- @Configuration
@ComponentScan({"com.bfxy.spring.*"})
public class RabbitMQConfig {- @Bean
public ConnectionFactory connectionFactory() {- CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}- @Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}- 26:Test使用rabbitAdmin
- @RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {- @Test
public void contextLoads() {
}- @Autowired
private RabbitAdmin rabbitAdmin;- @Test
public void testAdmin() throws Exception {
rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false));- rabbitAdmin.declareExchange(new TopicExchange("test.topic", false, false));
- rabbitAdmin.declareExchange(new FanoutExchange("test.fanout", false, false));
- rabbitAdmin.declareQueue(new Queue("test.direct.queue", false));
- rabbitAdmin.declareQueue(new Queue("test.topic.queue", false));
- rabbitAdmin.declareQueue(new Queue("test.fanout.queue", false));
- rabbitAdmin.declareBinding(new Binding("test.direct.queue",
Binding.DestinationType.QUEUE,
"test.direct", "direct", new HashMap<>()));- rabbitAdmin.declareBinding(
BindingBuilder
.bind(new Queue("test.topic.queue", false)) //直接创建队列
.to(new TopicExchange("test.topic", false, false)) //直接创建交换机 建立关联关系
.with("user.#")); //指定路由Key- rabbitAdmin.declareBinding(
BindingBuilder
.bind(new Queue("test.fanout.queue", false))
.to(new FanoutExchange("test.fanout", false, false)));- //清空队列数据
rabbitAdmin.purgeQueue("test.topic.queue", false);- }
- }
- 27:直接创建对应交换机 & 队列
- @Configuration
@ComponentScan({"com.bfxy.spring.*"})
public class RabbitMQConfig {- @Bean
public ConnectionFactory connectionFactory() {- CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}- @Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}- /**
* 针对消费者配置
* 1. 设置交换机类型
* 2. 将队列绑定到交换机
* FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
* HeadersExchange :通过添加属性key-value匹配
* DirectExchange:按照routingkey分发到指定队列
* TopicExchange:多关键字匹配
*/
@Bean
public TopicExchange exchange001() {
return new TopicExchange("topic001", true, false);
}- @Bean
public Queue queue001() {
return new Queue("queue001", true); //队列持久
}- @Bean
public Binding binding001() {
return BindingBuilder.bind(queue001()).to(exchange001()).with("spring.*");
}- @Bean
public TopicExchange exchange002() {
return new TopicExchange("topic002", true, false);
}- @Bean
public Queue queue002() {
return new Queue("queue002", true); //队列持久
}- @Bean
public Binding binding002() {
return BindingBuilder.bind(queue002()).to(exchange002()).with("rabbit.*");
}- @Bean
public Queue queue003() {
return new Queue("queue003", true); //队列持久
}- @Bean
public Binding binding003() {
return BindingBuilder.bind(queue003()).to(exchange001()).with("mq.*");
}- @Bean
public Queue queue_image() {
return new Queue("image_queue", true); //队列持久
}- @Bean
public Queue queue_pdf() {
return new Queue("pdf_queue", true); //队列持久
}- @Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
- }
28:测试直接创建对应交换机 & 队列通过rabbitTemplate发送消息
它提供了丰富的发送消息方法,包括可靠性投递消息方法、回调监听消息接口 ConfirmCallback、
返回值确认接口 ReturnCallback 等等。同样我们需要进行注入到 Spring 容器中,然后直接使用。
RabbitTemplate 在 Spring 整合时需要实例化,但是在 Springboot 整合时,在配置文件里添加配置即可
- @Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendMessage() throws Exception {
//1 创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put("desc", "信息描述..");
messageProperties.getHeaders().put("type", "自定义消息类型..");
Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
rabbitTemplate.convertAndSend("topic001", "spring.amqp", message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
System.err.println("------添加额外的设置---------");
message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述");
message.getMessageProperties().getHeaders().put("attr", "额外新加的属性");
return message;
}
});
}
@Test
public void testSendMessage2() throws Exception {
//1 创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq 消息1234".getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.abc", message);
rabbitTemplate.convertAndSend("topic001", "spring.amqp", "hello object message send!");
rabbitTemplate.convertAndSend("topic002", "rabbit.abc", "hello object message send!");
}
29:SimpleMessageListenerContailer
(1)简单消息监听容器:这个类非常的强大,我们可以对他进行很多设置,对于消费者的配置项,这个类都可以满足
(2)设置事务特性,事务管理器,事务属性,事务容量,事务开启等
(3)设置消息确认和自动确认模式,是否重回队列,异常捕获handler函数
(4)设置消费者标签生成策略,是否独占模式,消费者属性等
(5)simpleMessageListenerContailer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小,接收消息的模式等
- @Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
container.setConcurrentConsumers(1);
container.setMaxConcurrentConsumers(5);
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setExposeListenerChannel(true);
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID().toString();
}
});- container.setMessageListener(new ChannelAwareMessageListener() {
@Override public void onMessage(Message message, Channel channel) throws Exception {
String msg = new String(message.getBody());
System.err.println("----------消费者: " + msg);
}
});
}
30:MessageListenerAdapter 适配器
- 适配器方式. 默认是有自己的方法名字的:handleMessage
// 可以自己指定一个方法的名字: consumeMessage
// 也可以添加一个转换器: 从字节数组转换为String
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
adapter.setMessageConverter(new TextMessageConverter());
container.setMessageListener(adapter);
- MessageDelegate 类
- public class MessageDelegate {
- public void handleMessage(byte[] messageBody) {
System.err.println("默认方法, 消息内容:" + new String(messageBody));
}- public void consumeMessage(byte[] messageBody) {
System.err.println("字节数组方法, 消息内容:" + new String(messageBody));
}- public void consumeMessage(String messageBody) {
System.err.println("字符串方法, 消息内容:" + messageBody);
}
}
- TextMessageConverter 类
- public class TextMessageConverter implements MessageConverter {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return new Message(object.toString().getBytes(), messageProperties);
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
String contentType = message.getMessageProperties().getContentType();
if(null != contentType && contentType.contains("text")) {
return new String(message.getBody());
}
return message.getBody();
}
}- 31:配器方式: 我们的队列名称 和 方法名称 也可以进行一一的匹配
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setMessageConverter(new TextMessageConverter());
Map<String, String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queue001", "method1");
queueOrTagToMethodName.put("queue002", "method2");
adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
container.setMessageListener(adapter);
32:支持json格式的转换器
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");- Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
adapter.setMessageConverter(jackson2JsonMessageConverter);- container.setMessageListener(adapter);
- public class MessageDelegate {
- public void consumeMessage(Map messageBody) {
System.err.println("map方法, 消息内容:" + messageBody);
}
}
@Test
- public void testSendJsonMessage() throws Exception {
Order order = new Order();
order.setId("001");
order.setName("消息订单");
order.setContent("描述信息");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(order);
System.err.println("order 4 json: " + json);- MessageProperties messageProperties = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties.setContentType("application/json");
Message message = new Message(json.getBytes(), messageProperties);- rabbitTemplate.send("topic001", "spring.order", message);
}- 32:DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象转换
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");- Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
- DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);- adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
- public class MessageDelegate {
- public void consumeMessage(Order order) {
System.err.println("order对象, 消息内容, id: " + order.getId() +
", name: " + order.getName() +
", content: "+ order.getContent());
}- }
- @Test
public void testSendJavaMessage() throws Exception {- Order order = new Order();
order.setId("001");
order.setName("订单消息");
order.setContent("订单描述信息");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(order);
System.err.println("order 4 json: " + json);- MessageProperties messageProperties = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties.setContentType("application/json");
messageProperties.getHeaders().put("__TypeId__", "com.bfxy.spring.entity.Order");
Message message = new Message(json.getBytes(), messageProperties);- rabbitTemplate.send("topic001", "spring.order", message);
}- 33:DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象多映射转换
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();- Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("order", com.bfxy.spring.entity.Order.class);
idClassMapping.put("packaged", com.bfxy.spring.entity.Packaged.class);- javaTypeMapper.setIdClassMapping(idClassMapping);
- jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);- public class MessageDelegate {
public void consumeMessage(Order order) {
System.err.println("order对象, 消息内容, id: " + order.getId() +
", name: " + order.getName() +
", content: "+ order.getContent());
}- public void consumeMessage(Packaged pack) {
System.err.println("package对象, 消息内容, id: " + pack.getId() +
", name: " + pack.getName() +
", content: "+ pack.getDescription());
}
}
- @Test
public void testSendMappingMessage() throws Exception {- ObjectMapper mapper = new ObjectMapper();
- Order order = new Order();
order.setId("001");
order.setName("订单消息");
order.setContent("订单描述信息");- String json1 = mapper.writeValueAsString(order);
System.err.println("order 4 json: " + json1);- MessageProperties messageProperties1 = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties1.setContentType("application/json");
messageProperties1.getHeaders().put("__TypeId__", "order");
Message message1 = new Message(json1.getBytes(), messageProperties1);
rabbitTemplate.send("topic001", "spring.order", message1);- Packaged pack = new Packaged();
pack.setId("002");
pack.setName("包裹消息");
pack.setDescription("包裹描述信息");- String json2 = mapper.writeValueAsString(pack);
System.err.println("pack 4 json: " + json2);- MessageProperties messageProperties2 = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties2.setContentType("application/json");
messageProperties2.getHeaders().put("__TypeId__", "packaged");
Message message2 = new Message(json2.getBytes(), messageProperties2);
rabbitTemplate.send("topic001", "spring.pack", message2);
}- 34:配置全局的转换器
- MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
//全局的转换器:
ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
TextMessageConverter textConvert = new TextMessageConverter();
convert.addDelegate("text", textConvert);
convert.addDelegate("html/text", textConvert);
convert.addDelegate("xml/text", textConvert);
convert.addDelegate("text/plain", textConvert);
Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();
convert.addDelegate("json", jsonConvert);
convert.addDelegate("application/json", jsonConvert);- ImageMessageConverter imageConverter = new ImageMessageConverter();
convert.addDelegate("image/png", imageConverter);
convert.addDelegate("image", imageConverter);- PDFMessageConverter pdfConverter = new PDFMessageConverter();
convert.addDelegate("application/pdf", pdfConverter);
adapter.setMessageConverter(convert);
container.setMessageListener(adapter);
return container;
35:图片转换器
- public class ImageMessageConverter implements MessageConverter {
- @Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
throw new MessageConversionException(" convert error ! ");
}- @Override
public Object fromMessage(Message message) throws MessageConversionException {
System.err.println("-----------Image MessageConverter----------");- Object _extName = message.getMessageProperties().getHeaders().get("extName");
String extName = _extName == null ? "png" : _extName.toString();- byte[] body = message.getBody();
String fileName = UUID.randomUUID().toString();
String path = "d:/010_test/" + fileName + "." + extName;
File f = new File(path);
try {
Files.copy(new ByteArrayInputStream(body), f.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return f;
}- }
- @Test
public void testSendExtConverterMessage() throws Exception {
// byte[] body = Files.readAllBytes(Paths.get("d:/002_books", "picture.png"));
// MessageProperties messageProperties = new MessageProperties();
// messageProperties.setContentType("image/png");
// messageProperties.getHeaders().put("extName", "png");
// Message message = new Message(body, messageProperties);
// rabbitTemplate.send("", "image_queue", message);- byte[] body = Files.readAllBytes(Paths.get("d:/002_books", "mysql.pdf"));
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("application/pdf");
Message message = new Message(body, messageProperties);
rabbitTemplate.send("", "pdf_queue", message);
}- 36:spring boot整合 rabbitmq
producer端代码
- @Component
public class RabbitSender {- //自动注入RabbitTemplate模板类
@Autowired
private RabbitTemplate rabbitTemplate;- //回调函数: confirm确认
final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.err.println("correlationData: " + correlationData);
System.err.println("ack: " + ack);
if(!ack){
System.err.println("异常处理....");
}
}
};- //回调函数: return返回
final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
String exchange, String routingKey) {
System.err.println("return exchange: " + exchange + ", routingKey: "
+ routingKey + ", replyCode: " + replyCode + ", replyText: " + replyText);
}
};- //发送消息方法调用: 构建Message消息
public void send(Object message, Map<String, Object> properties) throws Exception {
MessageHeaders mhs = new MessageHeaders(properties);
Message msg = MessageBuilder.createMessage(message, mhs);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//id + 时间戳 全局唯一
CorrelationData correlationData = new CorrelationData("1234567890");
rabbitTemplate.convertAndSend("exchange-1", "springboot.abc", msg, correlationData);
}- //发送消息方法调用: 构建自定义对象消息
public void sendOrder(Order order) throws Exception {
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//id + 时间戳 全局唯一
CorrelationData correlationData = new CorrelationData("0987654321");
rabbitTemplate.convertAndSend("exchange-2", "springboot.def", order, correlationData);
}- }
- 测试代码
- @Autowired
private RabbitSender rabbitSender;- private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- @Test
public void testSender1() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("number", "12345");
properties.put("send_time", simpleDateFormat.format(new Date()));
rabbitSender.send("Hello RabbitMQ For Spring Boot!", properties);
}- @Test
public void testSender2() throws Exception {
Order order = new Order("001", "第一个订单");
rabbitSender.sendOrder(order);
}
- 36:spring boot整合 rabbitmq
consumer端代码
- @Component
public class RabbitReceiver {- @RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "queue-1",
durable="true"),
exchange = @Exchange(value = "exchange-1",
durable="true",
type= "topic",
ignoreDeclarationExceptions = "true"),
key = "springboot.*"
)
)
@RabbitHandler
public void onMessage(Message message, Channel channel) throws Exception {
System.err.println("--------------------------------------");
System.err.println("消费端Payload: " + message.getPayload());
Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
//手工ACK
channel.basicAck(deliveryTag, false);
}- /**
*
* spring.rabbitmq.listener.order.queue.name=queue-2
spring.rabbitmq.listener.order.queue.durable=true
spring.rabbitmq.listener.order.exchange.name=exchange-1
spring.rabbitmq.listener.order.exchange.durable=true
spring.rabbitmq.listener.order.exchange.type=topic
spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.order.key=springboot.*
* @param order
* @param channel
* @param headers
* @throws Exception
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "${spring.rabbitmq.listener.order.queue.name}",
durable="${spring.rabbitmq.listener.order.queue.durable}"),
exchange = @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}",
durable="${spring.rabbitmq.listener.order.exchange.durable}",
type= "${spring.rabbitmq.listener.order.exchange.type}",
ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
key = "${spring.rabbitmq.listener.order.key}"
)
)
@RabbitHandler
public void onOrderMessage(@Payload com.bfxy.springboot.entity.Order order,
Channel channel,
@Headers Map<String, Object> headers) throws Exception {
System.err.println("--------------------------------------");
System.err.println("消费端order: " + order.getId());
Long deliveryTag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
//手工ACK
channel.basicAck(deliveryTag, false);
}
}
- 37:集群模式
主备模式
实现RabbitMQ的高可用集群,一般在并发和数据量不高的情况下,这种模式非常的好且简单。主备模式也称为Warren模式
主节点如果挂了,备节点提供服务而已,和activemq利用zookeeper做主/备一样
- (主节点如果挂了,从节点提供服务而已,和activemq利用zookeeper做主/备一样)

- HaProxy配置:
listen rabbitmq_cluster
bind 0.0.0.0:5672
mode tcp #配置TCP模式
balance roundrobin #简单的轮询
server bhz76 192.168.11.12:5672 check inter 5000 rise 2 fall 3 #主节点
server bhz77 192.168.11.13:5672 backup check inter 5000 rise 2 fall 3 #备用节点
备注:
rabbitmq集群节点配置 #inter 每隔5秒对mq集群做健康检查,2次正确证明服务器可用,3次失败证明服务器不可用,并且配置主备机制
镜像模式(常用
)
镜像模式:集群模式非常经典的就是Mirror镜像模式,保证100%数据不丢失,在实际工作中用的最多的。并且实现集群非常的简单,一般互联网大厂都会构建这种镜像集群模式。
Mirror镜像队列,目的是为了保证rabbitmq数据的高可靠性解决方案,主要就是实现数据的同步,一般来讲是2-3个实现数据同步(对于100%数据可靠性解决方案一般是3个节点)集群架构如下:

多活模式
多活模式:这种模式也是实现异地数据复制的主流模式,因为Shovel模式配置比较复杂,所以一般来说实现异地集群都是使用双活或者多活模式来实现的。这种模式需要依赖rabbitmq的federation插件,可以实现继续的可靠AMQP数据通信,多活模式在实际配置与应用非常的简单。
RabbitMQ部署架构采用双中心模式(多中心),那么在两套(或多套)数据中心中各部署一套RabbitMQ集群,各中心之间还需要实现部分队列消息共享。多活集群架构如下:

Federation插件
是一个不需要构建Cluster,而在Brokers之间传输消息的高性能插件,Federation插件可以在Brokers或者Cluster之间传输消息,连接双方可以使用不同的users和vistual hosts,双方也可以使用版本不同的RabbitMQ和Erlang。Federation插件使用AMQP协议通信,可以接收不连续的传输。

- Federation Exchanges,可以看成Downstream从Upstream主动拉取消息,但并不是拉取所有消息,必须是在Downstream上已经明确定义Bindings关系的Exchange,也就是有实际的物理Queue来接收消息,才会从Upstream拉取消息到Downstream。使用AMQP协议实施代理间通信,Downstream会将绑定关系组合在一起,绑定/解绑命令将会发送到Upstream交换机。因此,FederationExchange只接收具有订阅的消息。
RabbitMQ集群镜像模式从0到1
RabbitMQ集群环境节点说明
详细步骤:
RabbitMQ镜像集群搭建步骤
HAProxy
是一款提供高可用性、负载均衡
以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,他是免费、快速并且可靠的一种解决方案。HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上,完全可以支撑数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中,同时可以保护你的web服务器不被暴露到网络上。- HAProxy借助于OS上几种常见的技术来实现性能的最大化:
- 单进程、时间驱动模型显著降低上下文切换的开销及内存占用
- 在任何可用的情况下,单缓冲(single buffering)机制能以不复制任何数据的方式完成读写操作,这会节约大量的CPU时钟周期及内存带宽
- 借助于Linux2.6上的splice()系统调用,HAProxy可以实现零复制转发(Zero-copy- forwarding),在linux3.5及以上的OS上还可以实现零复制启动(zero-starting)
KeepAlived
软件主要是通过VRRP协议实现高可用
功能的。VRRP是Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP出现的目的就是为了解决静态路由单点故障问题的,它能保证党个别节点宕机时,整个网络可以不间断地运行,所以,KeepAlived一方面具有配置管理LVS的功能,同时还具备对LVS下面节点进行健康检查差的功能,另一方面可实现系统网络服务的高可用功能。KeepAlived服务的三个重要功能:
- 管理LVS负载均衡软件
- 实现LVS集群节点的健康检查
- 作为系统网络服务的高可用性(failover)
KeepAlived高可用原理
KeepAlived高可用服务对之间的故障转移,是通过VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)来实现的。在KeepAlived服务正常工作是,主Master节点会不断地向备节点发送(多播的方式)心跳消息,用以告诉备Backup节点自己还活着,当主master节点发生故障时,就无法发送心跳消息,备节点也就因此无法继续监测到来自主Master节点的心跳了,于是调用自身的接管程序,接管主Master节点 的IP资源及服务。当主Master节点恢复时,备Backup节点又会释放主节点故障时自身接管的IP资源和服务,恢复到原来的备用角色。
- 40: 延迟插件
- 声明exchange类型为x-delayed-message,
声明queue的参数中增加x-dead-letter-exchange,
消息头部增加x-delay参数,
- 41:消息发送模式
- 迅速消息 不做可靠性投递 不做消息落库
- 批量消息发送 把消息放到一个集合里面统一进行提交
延迟消息发送
顺序消息发送
必须保证所有消息投递到一个队列中且这个队列只能有一个消费者独占模式
rabbitmq消息中间件读后感的更多相关文章
- spring boot / cloud (九) 使用rabbitmq消息中间件
spring boot / cloud (九) 使用rabbitmq消息中间件 前言 rabbitmq介绍: RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.它可以用于大型软件系统 ...
- RabbitMQ 消息中间件
RabbitMQ 是使用 Erlang 语言开发的消息中间件, 其遵循了高级消息队列协议(Advanced Message Queuing Protocol, AMQP). 与 Kafka 等消息队列 ...
- CentOS6.8搭建rabbitmq消息中间件
参考资料:http://blog.csdn.net/yunfeng482/article/details/72853983 一.rabbitmq简介 MQ全称为Message Queue, 消息队列( ...
- python中使用rabbitmq消息中间件
上周一直在研究zeromq,并且也实现了了zeromq在python和ruby之间的通信,但是如果是一个大型的企业级应用,对消息中间件的要求比较高,比如消息的持久化机制以及系统崩溃恢复等等需求,这个时 ...
- RabbitMQ消息中间件的用法
1.什么是RabbitMQ RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消 ...
- Linux(Centos平台)RabbitMQ消息中间件服务器搭建
本篇结合接口测试平台部署来讲,不了解的请先查看我的另一篇文档,HttpRunnerManager接口测试平台部署在服务器上(Centos + python3.6 + Mysql5.7 + uwsgi ...
- RabbitMQ消息中间件极速入门与实战
1:初识RabbitMQ RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AM ...
- Laravel框架安装RabbitMQ消息中间件步骤
Laravel5.6 整合 RabbitMQ 消息队列 简介: Laravel 队列为不同的后台队列服务提供了统一的 API,例如 Beanstalk,Amazon SQS,Redis,甚至其他基于关 ...
- springcloud 连接docker中运行的RabbitMQ消息中间件。
参考:https://blog.51cto.com/zero01/2173288 主要是记录几个坑: 第一个坑:开始订单服务中配置文件是: #配置rabbitmq 2019.5.17 added by ...
随机推荐
- sql 创建用户脚本
USE master go CREATE LOGIN jiazhuang --用户名 WITH PASSWORD = 'sa', --密码 DEFAULT_DATABASE = JiaZhuan, ...
- 获取当前的日期时间的js函数,格式为“yyyy-MM-dd hh:mm:ss”
//获取当前的日期时间函数,格式为“yyyy-MM-dd hh:mm:ss” function getNowFormatDate(date) { if (date == null) { var dat ...
- 那些你常用的JSP知识
脚本程序 <> 或者,您也可以编写与其等价的XML语句,就像下面这样: <jsp:scriptlet> 代码片段 </jsp:scriptlet>任何文本.HTML ...
- final关键字介绍
许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”.常数主要应用于下述两个方面: (1) 编译期常数,它永远不会改变 (2) 在运行期初始化的一个值,我们不希望它发生变化 对于编译期的常数,编 ...
- ssh命令使用
ssh是什么 ssh是一种通信协议,用于与另一台计算机通信,有多个实现版本,openssh属于开源实现 usage: ssh [-B bind_interface] [-b bind_address] ...
- 支持触屏的jQuery轮播图插件
移动轮播图我看到两类, 一款是无线天猫的m.tmall.com和携程,实现了无缝轮播. 一款是蘑菇街的,没有实现无缝轮播. 我自己重写一个,类似天猫. 1.页面代码 <!DOCTYPE html ...
- Stage2--Python的数据类型
说在前面: Stage1-Stage4简单介绍一下Python语法,Stage5开始用python实现一些实际应用,语法的东西到处可以查看到,学习一门程序语言的最终目的是应用,而不是学习语法,语法本事 ...
- MicroService
- jdbc、Connection pool、jndi的理解和关系
一.概念和理解: ①.jdbc:Java Data Base Connectivity,java数据库连接,最为传统的一种方式,直接连接操作数据库,需要连接时创建连接,使用结束时销毁连接. ②.Con ...
- java面试题----String、StringBuffer、StringBudder区别
面试题1 - 什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好? 面试题2 - 请说出下面程序的输出. class ...