这里只介绍基于CentOS7的docker单机部署。
在线拉取
docker pull rabbitmq:3-management
执行下面的命令来运行MQ容器:
docker run \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
--name rabbitmq \
--hostname mq1 \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management
可能发送消息丢失的几种情况:
因此要确保消息的可靠性,就需要做到以下几点:
RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程中丢失。消息发送到MQ后,会返回一个结果给发送者,表示消息是否处理成功。
注意:确认机制发送消息时,需要给每个消息设置一个全局唯一id,以区分不同的消息,避免ack冲突。
SpringAMQP实现生产者确认
spring:
rabbitmq:
publisher-confirm-type: correlated
publisher-returns: true
template:
mandatory: true
@Sjf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey) -> {
log.info("消息发送失败,应答码{},原因{},交换机{},路由键{},消息{}",replyCode,replyText,exchange,routingKey,message.toString());
});
}
}
消息默认实在内存中存储,MQ宕机的时候自定义的交换机和队列都会消失,消息自然而然也会丢失。交换机和队列在声明的时候可以指定持久化。
@Bean
public Queue orderSeckillOrderQueue() {
//四个参数分别为:队列名称、是否持久化、是否排他、是否自动删除
return new Queue(MQConstant.ORDER_SECKILL_ORDER_QUEUE, true, false, false);
}
@Bean
public Exchange orderEventExchange() {
//三个参数分别为:交换机名称、是否持久化、是否自动删除【true->当没有队列绑定的时候就会删除】
return new TopicExchange(MQConstant.ORDER_EVENT_EXCHANGE, true, false);
}
消息也可以进行持久化,
Message message = MessageBuilder.withBody("message".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT) //持久化
.build();
注意:其实默认声明的交换机、队列和消息都是持久的,可以不用单独去配置。
手动确认消息代码
@RabbitHandler
public void listener(SeckillOrderTo seckillOrder, Channel channel, Message message) throws IOException {
try {
log.info("准备创建秒杀订单的详细信息。。。");
orderService.createSeckillOrder(seckillOrder);
//手动确认消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (IOException e) {
//出现异常拒绝消息,重新放回队列,如果一直异常就会一直重试,需要优化
channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
}
}
注意:此种失败重试机制只适合在消费者自动确认模式下使用,如果是手动确认的话,消息路由到error.queue后,在原队列里还会存在并且是Unacked状态。
当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。最早接收到的消息,就可能会成为死信,会被丢弃,这就是消息堆积的问题。
解决消息堆积的三种思路:
RabbitMQ从3.6.0版本开始就加入了Lazy Queues的概念,也就是惰性队列。
惰性对了特性如下:
注意
普通队列,会先将消息存到内存,然后再刷到磁盘,存到磁盘的操作叫做Paged Out。
而惰性队列,不会经过内存,会直接将消息存到磁盘,存到磁盘的操作叫做Paged Out。
模仿普通队列高并发下接收消息:
模仿惰性队列高并发下接收消息:
如何设置惰性队列?
设置镜像队列的方式:
在随便一个节点上执行以下命令
rabbitmqctl set_policy -p /test ha-all "^all\." '{"ha-mode":"all"}'
-p /test 指定虚拟机
ha-all 策略名称
^all. 正则表达式,代表所有以all开头的队列
ha-mode:all 指定镜像队列的模式,这里是all,也就是集群中所有的节点都作为从节点。
docker exec -it rabbitmq cat /var/lib/rabbitmq/.erlang.cookie
EZDJGLMTEWCVVAYLCLRD
docker rm -f rabbitmq
mkdir -p /mydata/rabbitmq
cd rabbitmq
touch rabbitmq.conf
loopback_users.guest = false
listeners.tcp.default = 5672
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = rabbit@node1
cluster_formation.classic_config.nodes.2 = rabbit@node2
cluster_formation.classic_config.nodes.3 = rabbit@node3
touch .erlang.cookie
echo “EZDJGLMTEWCVVAYLCLRD” > .erlang.cookie
chmod 600 .erlang.cookie
docker run -e RABBITMQ_DEFAULT_USER=itcast -e RABBITMQ_DEFAULT_PASS=123321 --name rabbitmq --hostname mq1 -p 8081:15672 -p 8071:5672 -d rabbitmq:3.8-management
docker run -d
-v /mydata/rabbitmq/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie
-e RABBITMQ_DEFAULT_USER=hupp
-e RABBITMQ_DEFAULT_PASS=mq123_
–name mq1
–hostname mq1
-p 8071:5672
-p 8081:15672
rabbitmq:3.8-management
docker run -d
-v /mydata/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
-v /mydata/rabbitmq/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie
-e RABBITMQ_DEFAULT_USER=hupp
-e RABBITMQ_DEFAULT_PASS=mq123_
–name mq2
–hostname mq2
-p 8072:5672
-p 8082:15672
rabbitmq:3.8-management
docker run -d
-v /mydata/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
-v /mydata/rabbitmq/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie
-e RABBITMQ_DEFAULT_USER=hupp
-e RABBITMQ_DEFAULT_PASS=mq123_
–name mq3
–hostname mq3
-p 8073:5672
-p 8083:15672
rabbitmq:3.8-management
docker run -d --net mq-net \
-v /mydata/mq1/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-v /mydata/mq1/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
-e RABBITMQ_DEFAULT_USER=hupp \
-e RABBITMQ_DEFAULT_PASS=mq123_ \
--name mq1 \
--hostname mq1 \
-p 8071:5672 \
-p 8081:15672 \
rabbitmq:3.8-management
docker run -d --net mq-net \
-v /mydata/mq2/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-v /mydata/mq2/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
-e RABBITMQ_DEFAULT_USER=hupp \
-e RABBITMQ_DEFAULT_PASS=mq123_ \
--name mq2 \
--hostname mq2 \
-p 8072:5672 \
-p 8082:15672 \
rabbitmq:3.8-management
docker run -d --net mq-net \
-v /mydata/mq3/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-v /mydata/mq3/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
-e RABBITMQ_DEFAULT_USER=hupp \
-e RABBITMQ_DEFAULT_PASS=mq123_ \
--name mq3 \
--hostname mq3 \
-p 8073:5672 \
-p 8083:15672 \
rabbitmq:3.8-management