一 集群简介

1 集群形式

  • RabbitMQ是用Erlang开发的,集群很容易,因为Erlang天生就是一门分布式语言,但其本身并不支持负载均衡,需要自己配置(可以借助nginx)。
  • RabbitMQ 集群中节点包括内存节点(RAM),磁盘节点(Disk,消息持久化),集群中至少有一个Disk节点

2 普通模式(默认)

对于普通模式,集群中的各节点有相同的队列结构,但是消息只会存在于集群中那个的一个节点。对于消费者来 说,若消息进去A的Queue,但是消费者从B拉去的时候,B的Queue中是没有消息的,此时要将A中的消息发送给B,然后在发送给消费者

应用场景: 该模式适合于消息无需持久化的场合,如日志队列,当队列非持久化,且创建该队列的节点宕机,客户端才可以重连集群其他节点,并重新创建队列,若为持久化,只能等待故障节点的恢复。

3 镜像模式

与普通模式不同之处是消息实体主动在镜像节点同步,而不是取数据时临时拉取,高可用,该模式下,mirror queue 有一条选举算法,即 1个master,n个slave,生产者消费者的请求都会转至master

应用场景: 可靠性要求较高的场合,如下单,库存队列 缺点: 若镜像队列过多,且消息体量大,集群内部网络带宽将会被此种同步通讯所消耗。

1 镜像集群也是基于普通集群,即先搭建普通集群,然后才能设置镜像队列 2 若消费过程中,master挂掉,则选举新master,若未来的及确认,则可能出现重复消费

补: 消息重复消费的问题

问题出现场景: 当消费者消费消息后,开始对消息进行确认收到已经消费的时候,向MQ发送消息,此时如果网络不稳定,或者MQ的master挂掉,此时就会出现本地消费者已经消费了消息,但是此时的MQ并没有收到消费者关于确认收到消息的ack,此时MQ就会把消息还保存到队列中;当网络稳定后或者master重新拉起,消费者就会再次收到相同的消息消费,就会出现消息重复消费。

解决办法:

可以在每次消费收到后,还未被处理的时候,可以拿到消息的id【messageId】,放入redis或者Mysql中,进行判断,如果存在messageId,就直接确认消息,不处理消息;如果没有,就处理消息,然后确认消息已经收到

@RabbitListener(queues = "test.queue")
public void upBlog(MessageDTO messageDTO Message message, Channel channel) {
    long deliveryTag = message.getMessageProperties().getDeliveryTag(); // 获取消息的唯一ID
    String messageId=redis.get("deliveryTag"); // 也可以使用redis的list
    if(messageId==null){
        // 1 执行业务方法
        Boo xxxService.save(messageDTO);
        if(flag){
            // 2 确认消息
            channel.basicAck(deliveryTag, false);
            // 3 向redis中放入消息id
            redis.set("deliveryTag",deliveryTag);
        }else{
            // 业务处理失败,消息重新入队
            channel.basicReject(deliveryTag, true);
        }
    }else{
        // 直接确认消息
        channel.basicAck(deliveryTag, false);
    }
}

二 搭建集群

1 创建容器,搭建普通集群

(1) 创建容器

docker run -d --hostname rabbitmq01  --name rabbitmq01 -v /data/es-rabbitmq/rabbitmq01:/var/lib/rabbitmq  -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='cloneman' rabbitmq:3-management

--hostname rabbitmq01 # 给rabbitmq 起一个主机名 RABBITMQ_ERLANG_COOKIE='cloneman' # 给定一个cookie,是每个rabbitmq节点加入集群的关键

docker run -d --hostname rabbitmq02  --name rabbitmq02 -v /data/es-rabbitmq/rabbitmq02:/var/lib/rabbitmq  -p 15674:15672 -p 5674:5672 -e RABBITMQ_ERLANG_COOKIE='cloneman' --link rabbitmq01:rabbitmq01  rabbitmq:3-management

--link rabbitmq01:rabbitmq01 # 告诉rabbitmq02 可以访问rabbitmq01

docker run -d --hostname rabbitmq03  --name rabbitmq03 -v /data/es-rabbit/rabbitmq03:/var/lib/rabbitmq  -p 15675:15672 -p 5675:5672 -e RABBITMQ_ERLANG_COOKIE='cloneman' --link rabbitmq01:rabbitmq01  --link rabbitmq02:rabbitmq02  rabbitmq:3-management

(2) 初始化集群内部属性

docker exec -it rabbitmq01 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit

docker exec -it rabbitmq02 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit

docker exec -it rabbitmq03 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit

RabbitMQ集群搭建_集群

2 升级集群为镜像集群

docker exec -it rabbitmq01 /bin/bash
rabbitmqctl set_policy -p / clo_ha  "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
rabbitmqctl  list_policies -p / # 查看策略

RabbitMQ集群搭建_RabbitMQ_02

set_policy -p / clo_ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
/ 为 vhost clo_ha 为 策略的名字 '^' 所有的队列 "ha-mode":"all" 所有队列都是高可用,all为复制所有的节点,包括新增节点 "ha-sync-mode":"automatic" 集群中队列信息自动同步

  • 创建队列,发送消息,查看是否每个节点队列都有消息,如果都有,那么就搭建镜像集群成功