RabbitMQ

一、RabbitMQ与springboot:Springamqp

1.引入依赖

<!--SpringAMQP,内含rabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2.在publisher中编写测试方法,向simple.queue发送消息

二、工作模型:fanout、direct、topic

Exchange交换机:

        接收publisher发送的消息

        将消息按照规则路由到与之绑定的队列

        不能缓存消息、路由失败,消息丢失

        FanoutExchange会将消息路由到所有与之绑定的队列

1.SpringAMQP-FanoutExchange

(1)在consumer中常见的一个类,添加@Configuration注解,并且声明FanoutExchange、Queue和绑定关系对象Binding,代码如下

@Configuration
public class FanoutConfig {

    //声明交换机fanoutExchange
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("hwt.fanout");
    }

    //声明第一个队列1
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("hwt.queue1");
    }

    //声明第二个队列2
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("hwt.queue2");
    }

    //将交换机和队列1进行绑定
    @Bean
    public Binding bindingQueue1(Queue fanoutQueue1,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }
    
    //将交换机和队列2进行绑定
    @Bean
    public Binding bindingQueue2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }

}

(2)在consumer中写一个listener进行监听定义的交换机绑定的队列

@Component
public class SpringRabbitListener {

    /*
        Fanout交换机,会将消息发送到每个与它绑定的队列queue
     */
    @RabbitListener(queues = "hwt.queue1")
    public void listenFanoutQueue1(String msg) throws InterruptedException {
        System.out.println("消费者1接收到消息【"+msg+"】"+ LocalTime.now());
        Thread.sleep(200);
    }

    @RabbitListener(queues = "hwt.queue2")
    public void listenFanoutQueue2(String msg) throws InterruptedException {
        System.out.println("消费者2接收到消息【"+msg+"】"+ LocalTime.now());
        Thread.sleep(200);
    }

}

(3)编写测试类进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //fanout模式测试
    @Test
    public void  testFanoutQueue(){
        //交换机
        String exchange = "hwt.fanout";
        //发送信息
        String message = "hello:everyone";
        //参数:交换机、rountingKey、信息
        rabbitTemplate.convertAndSend(exchange,"",message);
    }

}

(4)启动consumer的应用,登录rabbitMQ可以看到所定义的交换机(hwt.fanout)以及进行绑定的queue

(5)执行测试,消息消费之后会在rabbitMQ中自动删除

2.SpringAMQP-DirectExchange

发布订阅-DirectExchange

direct交换机会将publisher发送的消息接收到后,根据规则路由到指定的队列Queue,因此也成为路由模式(route)。

Queue都与Exchange设置一个BindingKey

发布者发送消息时,指定消息的RoutingKey

DirectExhcange将消息路由到BindingKey与消息的RountingKey匹配一致的队列Queue。

(1)在consumer服务中声明Exchange、Queue

@Component
public class SpringRabbitListener {

    //绑定Queue、exchange、routingKey
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "hwt.direct",type = ExchangeTypes.DIRECT),
            key = {"blue","red"}
    ))
    public void listenDirectQueue1(String msg){
        System.out.println("DirectQueue1接收到消息:"+msg+" time:"+LocalTime.now());
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("direct.queue2"),
            exchange = @Exchange(name = "hwt.direct",type = ExchangeTypes.DIRECT),
            key = {"red","yellow"}
    ))
    public void listenDirectQueue2(String msg){
        System.out.println("DirectQueue2接收到消息:"+msg+" time:"+LocalTime.now());
    }

}

(2)启动consumer所在的服务,可以看到Exchange以及绑定的Queue以及RoutingKey

(3)编写测试类,指定rountingKey

    @Test
    public void testDirectExchange(){
        //direct交换机
        String exchange = "hwt.direct";
        //发送信息
        String message = "direct:hello,everyone";
        rabbitTemplate.convertAndSend(exchange,"yellow",message);
    }

DirectExchange与FanoutExchange的区别:

FanoutExcahnge将消息路由到每一个与它绑定的队列

DirectExchange会根据RoutingKey判断路由给哪个队列

如果多个队列具有相同的RoutingKey,则与FanoutExchange相同

3.SpringAMQP-TopicExchange

TopicExchange与DirectExchange类似,区别在于RountingKey必须是多个单词的列表,用"."分割

Queue与Exchange设置的BindingKey可以使用通配符:

#:代指0个或者多个单词

*:代指一个单词

(1)在consumer中声明Exchange、Queue、RoutingKey

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("topic.queue1"),
            exchange = @Exchange(value = "hwt.topic",type = ExchangeTypes.TOPIC),
            key = {"china.#"}
    ))
    public void listenTopicQueue1(String msg){
        System.out.println("TopicQueue1接收到消息:"+msg+" time:"+LocalTime.now());
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue("topic.queue2"),
            exchange = @Exchange(value = "hwt.topic",type = ExchangeTypes.TOPIC),
            key = {"#.news"}
    ))
    public void listenTopicQueue2(String msg){
        System.out.println("TopicQueue2接收到消息:"+msg+" time:"+LocalTime.now());
    }

(2)启动consumer所在的服务,可以看到Exchange以及绑定的Queue以及RoutingKey

(3)编写测试类,指定rountingKey

    @Test
    public void testTopicExchange(){
        String exchange = "hwt.topic";
        String message = "topic:hello,china";
        rabbitTemplate.convertAndSend(exchange,"china.weather",message);
    }

三、消息转换器

Spring对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的,而默认实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。

如果要修改只需要定义一个MessageConverter类型的Bean即可,例如Json方式序列化

(1)在publisher中引入依赖

<!-- jackson序列化-->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.10</version>
</dependency>

(2)在publisher中声明MessageConverter

@SpringBootApplication
public class CustomApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomApplication.class,args);
    }

    @Bean
    public MessageConverter jsonMessageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

(3)然后定义一个消费者,监听object.queue的队列并消费消息

    @RabbitListener(queues = "object.queue")
    public void listenObjectQueue(Map<String,Object> msg){
        System.out.println("收到消息:    ["+msg+"]");
    }

(4)编写测试

    @Test
    public void testObjectQueue(){
        Map<String,Object> message = new HashMap<>();
        message.put("name","吴一凡");
        message.put("work","缝纫机员工");
        rabbitTemplate.convertAndSend("object.queue",message);
    }

四、MQ常见问题

消息可靠性问题:

        如何确保发送的消息至少被消费一次

消息延迟问题:

        如何实现消息的延迟投递

消息堆积问题:

        如何解决数百万消息堆积,无法及时消费问题

高可用:

        如何避免单点的MQ故障而导致的不可用问题

1.消息可靠性问题

消息从生产者发送到exchange,再到queue,再到消费者,有哪些导致消息丢失的可靠性?

> 发送时丢失

        生产者publisher发送的消息未到达交换机exchange

        生产者到达exchange但未到达queue

> MQ宕机,queue将消息丢失

> consumer接收到消息但未消费就宕机了

生产者确认机制

RabbitMQ提供了publisher confirm机制来避免消息发送到MQ的过程中丢失。消息发送到MQ后,会返回一个结果给发送者,表示消费是否处理成功,结果有两种请求:

> publisher confirm 发送者确认

        消息成功投递到交换机,返回ack

        消息未投递到交换机,返回nack

> publisher return,发送者回执

        消息投递到交换机了,但是没有路由到队列。发回ack及路由失败原因。

注意:消息确认机制发送消息时,需要给每一个消息设置一个全局唯一id,以区分不同消息,避免ack冲突。

(1)在publisher应用的application.yml中添加配置

spring:
  rabbitmq:
    publisher-confirm-type: correlated #异步回调 confirmCallback,同步调用 simple
    publisher-returns: true #开启publish-return功能,基于callback机制,需要定义ReturnCallback
    template:
      mandatory: true #开启消息路由失败时的策略,调用ReturnCallback,false:直接丢弃消息

(2)配置ReturnCallback

/*
    生产者确认:开启publish-return功能,每个RabbitTemplate只能配置一个ReturnCallback,因此需要在项目启动过程中配置
 */
@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
    @Override
    @Deprecated
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取RabbitTemplate
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        //设置ReturnCallback
        rabbitTemplate.setReturnCallback((message, replayCode, replayTest, exchange, routingKey) ->  {
                log.info("消息发送失败:应答码{},原因{},交换机{},路由键{},消息{}",
                        replayCode,replayTest,exchange,routingKey,message.toString());
        });
    }
}

(3)发送消息,指定消息ID,消息ConfirmCallback

    //发送消息,指定消息ID,消息confirmCallback
    @Test
    public void TestConfirmCallback(){

        //1.准备消息
        String message = "生产者确认机制:I am coming";
        //2.准备CorrelationData
        CorrelationData correlationData = new CorrelationData();
        //2.1消息ID
        correlationData.setId(UUID.randomUUID().toString());
        //2.2准备ConfirmCallback
        correlationData.getFuture().addCallback(result -> {
            if (result.isAck()) {
                //消息成功ACK
                log.debug("消息发送成功,ID{}", correlationData.getId());
            } else {
                //消息失败NACK
                log.error("消息发送失败,ID{}", correlationData.getId(),result.getReason());
            }
        }, ex -> log.error("消息发送异常,ID{}",correlationData.getId(),ex.getMessage()));
        //3.发送消息
        rabbitTemplate.convertAndSend("amq.topic","callback.test",message,correlationData);
    }

消息持久化

MQ默认是内存存储模式,开启持久化功能可以确保缓存在MQ中的消息不丢失

(1)消息持久化

    /*
        交换机持久化  (默认持久)
    */
    @Bean
    public DirectExchange durableExchange(){
        //三个参数:1.交换机名称,2.是否持久化,3.当没有Queue绑定时是否自动删除
        return new DirectExchange("durable.Exchange",true,false);
    }

(2)队列持久化

    /*
        队列持久化  (默认持久)
    */
    @Bean
    public Queue simpleQueue(){
        //使用QueueBuilder构建队列,durable是持久化的
        return QueueBuilder.durable("durable.queue").build();
    }

(3)消息持久化,SpringAMQP中的消息默认是持久的,可以通过MessageProperties中的DeliveryMode来指定

   //1.准备消息
   String msg= "durable.message:I am coming";
   Message message = MessageBuilder
.withBody(msg.getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();

消费者确认机制

RabbitMQ支持消费者确认机制,即消费者收到消息后可以向MQ发送回执ack,MQ收到回执ack后才删除该消息,而SpringAMQ允许配置三种确认模式

manual:手动ack,需要在处理业务逻辑之后,手动调用api发送ack。

auto:自动ack,由spring监测Listener代码是否出现异常,无异常返回ack,抛出异常返回nack。

none:关闭ack,MQ假定消费者收到消息后会成功处理,因此消息投递后立即删除

配置修改application.yml即可:

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1
        acknowledge-mode: auto #自动ack,none,关闭ack,#manual,手动ack

消费者失败重试机制

当消费者出现异常后,消息会重新入队requeue,再重新发送到消费者,然后再次异常,再次requeue,无限循环,导致MQ消息处理飙升,带来不必要的压力。

我们可以利用spring中的retry机制,在消费者出现异常后在本地重试,而不是无限制的requeue到MQ队列

配置如下:

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1
        retry:
          enabled: true #开启消费者失败重试
          initial-interval: 1000 #初始的重试等待时长为1秒
          multiplier: 3 #下次重试的的等待时长倍数,下次等待时长 = multiplier * initial-interval
          max-attempts: 3 #最大重试次数
          stateless: true #无状态,如果业务中包含事务,改为false,有状态

消费者失败消息处理策略

在开启重试模式之后,重试次数耗尽,如果消息依然失败,则需要有MessageRecoverer接口处理,它包含三种不同的实现:

> RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息(默认)。

> ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队。

> RepublishMessageRecoverer:重试耗尽后,将失败消息投递至指定的交换机。

RepublishMessageRecoverer处理策略示例:

(1)在consumer中常见的一个类,添加@Configuration注解,声明异常消息的处理类

@Configuration
public class ErrorMessageConfig {

    //异常交换机
    @Bean
    public DirectExchange errorMessageExchange(){
        return new DirectExchange("error.direct");
    }
    //异常交换机需要绑定的队列
    @Bean
    public Queue errorQueue(){
        return new Queue("error.queue");
    }

    //交换机与队列绑定
    @Bean
    public Binding errorMessageBinging(){
        return BindingBuilder.bind(errorQueue()).to(errorMessageExchange()).with("error");
    }
    //失败消息重新投至交换机,因为交换机只能转发消息,不能存储消息,所以前面声明了异常队列error.queue,由队列进行存储
    @Bean
    public MessageRecoverer republishMessageRecover(RabbitTemplate rabbitTemplate){
        return new RepublishMessageRecoverer(rabbitTemplate,"error.direct","error");
    }
}

(2)启动consumer应用,可以看到异常处理交换机以及绑定的队列

(3)异常处理策略测试

    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueue(String msg){
       log.debug("消费者接收到durable.queue的消息:【"+msg+"】");
        System.out.println(1/0);
        log.info("消费者处理消息成功");
    }

发送消息

查看控制台

查看error.queue队列

消息可靠性总结:

如何确保消息可靠性:

(1).开启生产者确认机制,确保生产者的消息可以到达队列

(2).开启持久化功能,确保消息未消费前在队列不会消失

(3).开启消费者确认机制auto,由spring确认消息处理完成后返回ack

(4).开启消费者失败重试机制,并且设置Messagerecoverer,多次重试失败后,将消息投递至异常交换机,交由人工处理

2.消息延迟性问题

当队列中消息满足以下的情况之一,就会成为死信(dead letter)。

> 消费者使用basic.reject或者basic.nack声明消费失败,并且消息的requeue参数设置为false。

> 消息是一个过期消息,超时无人消费。

> 要投递的队列消息堆满了,最早的消息可能成为死信。

如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到

这个交换机,而这个交换机就被称为死信交换机(Dead Letter Exchange 简称:DLX)。

(1)声明死信交换机和队列

    /*
        TTL     time-to-live
        监听延迟i消息(死信)
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "dl.queue",durable = "true"),
            exchange = @Exchange(name = "dl.direct"),
            key = "ddl"
    ))
    public void listenerDlQueue(String msg){
        log.info("接收到dl.queue的延迟消息:{}",msg);
    }

(2)给队列设置超时时间,并给队列指定私信交换机

@Configuration
public class TTLMessageConfig {

    @Bean
    public DirectExchange ttlExchange(){
        return new DirectExchange("ttl.direct");
    }

    @Bean
    public Queue ttlQueue(){
        return QueueBuilder
                .durable("ttl.queue")    //指定队列名称以及持久化
                .ttl(10000) //队列超时时间,10秒
                .deadLetterExchange("dl.direct")    //配置死信交换机
                .deadLetterRoutingKey("ddl")    //指定死信 RoutingKey
                .build();
    }

    @Bean
    public Binding simpleBinding(){
        return BindingBuilder.bind(ttlQueue()).to(ttlExchange()).with("ttl");
    }
}

(3)发送消息时,也可以给消息设置超时时间

    @Test
    public void  testTTLMessage(){
       //创建消息
        Message message = MessageBuilder.withBody("ttl message".getBytes(StandardCharsets.UTF_8))
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .setExpiration("5000")
                .build();
        //消息Id,需要封装到CorrelationData
        CorrelationData correlationData = new CorrelationData();
        correlationData.setId(UUID.randomUUID().toString());

        //发送消息
        rabbitTemplate.convertAndSend("ttl.direct","ttl",message);
        log.info("消息发送成功");
    }

延迟队列

利用TTL,结合死信交换机,我们实现了消息发出后,消费者延迟收到的效果,这种消息模式就称为延迟模式(Delay Queue)。

延迟队列使用场景包括:

> 延迟发送短信

> 用户下单,15min内未支付,自动取消订单。

> 预约工作会议,20min后自动通知所有参会人员

(1)安装DelayExchange插件

下载的插件放到plugins目录下后

执行:rabbitmq-plugins enable rabbitmq_delayed_message_exchange

(2)声明延迟交换机

可以使用RabbitMQ管理平台声明:

DelayExchange交换机本质还是官方的三种交换机,只是增加了延迟功能。因此使用时,只需要声明一个交换机,交换机的类型可以是任何类型,然后设定delayed属性为true即可。

基于注解方式

    /*
        延迟队列 DelayExchange
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "delay.queue",durable = "true"),
            exchange = @Exchange(name = "delay.direct",delayed = "true"),
            key = "delay"
    ))
    public void listenerDelayQueue(String msg){
        log.info("接收到delay.queue的延迟消息:{}",msg);
    }

基于代码方式

    @Bean
    public DirectExchange delayExchange(){
        return ExchangeBuilder
                .directExchange("delay.direct")//指定交换机类和名称
                .delayed()//设置delayed属性为true
                .durable(true)//持久化
                .build();
    }
    
    @Bean
    public Queue delayQueue(){
        return new Queue("delay.queue");
    }
    
    @Bean
    public Binding delayBinding(){
        return  BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay");
    }

然后我们向delayed为true的交换机发送消息,一定要给消息添加一个head:x-delay,值为延迟的时间,单位为毫秒

    /*
        延迟消息测试
     */
    @Test
    public void testDelayMessage(){
        Message message = MessageBuilder
                .withBody("hello,this is a delay message".getBytes(StandardCharsets.UTF_8))
                .setHeader("x-delay", 10000)//延迟时间,毫秒
                .build();

        CorrelationData correlationData = new CorrelationData();
        correlationData.setId(UUID.randomUUID().toString());

        rabbitTemplate.convertAndSend("delay.direct","delay",message,correlationData);
        log.debug("发送消息成功");
    }

忽略延迟消息的错误提示

@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
    @Override
    @Deprecated
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取RabbitTemplate
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        //设置ReturnCallback
        rabbitTemplate.setReturnCallback((message, replayCode, replayTest, exchange, routingKey) ->  {
            //判断是否是延迟消息
            if(message.getMessageProperties().getReceivedDelay()>0){
                //是延迟消息,忽略这个提示
                return;
            }

                log.info("消息发送失败:应答码{},原因{},交换机{},路由键{},消息{}",
                        replayCode,replayTest,exchange,routingKey,message.toString());
        });

    }

}

2.消息堆积问题

当生产者发送消息的速度大于消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储达到上限。最早收到的消息就会成为死信,会被丢弃,这就是消息堆积问题。

解决消息堆积有三种思路:

(1)增加更多消费者,提高消费速度。

(2)在消费者内开启线程池增加消息处理速度。

(3)扩大队列容积,提高堆积上线。

惰性队列

RabbitMQ从3.6起,就增加了Lazy Queues的概念,即惰性队列。

惰性队列有如下特征:

(1)接收到消息后直接存入磁盘而不是内存

(2)消费者要消费时,才会从磁盘中读取加入到内存

(3)支持数百万条数据存储

需要设置一个队列为惰性队列,只需要在声明队列时,指定x-queue-mode属性为lazy即可。可以通过命令将一个运行中的队列修改为惰性队列。

rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"lazy"}' -apply-to queues

(1)用SpringAMQP声明惰性队列分两种方式

> Bean方式

    /*
        惰性队列
     */
    @Bean
    public Queue lazyQueue(){
        return QueueBuilder
                .durable("lazy.queue")
                .lazy()
                .build();
    }

> 注解方式

     /* 
        注解声明:惰性队列
     */
    @RabbitListener(queuesToDeclare = @Queue(
            name = "lazy.queue",
            durable = "true",
            arguments = @Argument(name = "x-queue-mode",value = "lazy")
    ))
    public void listenLazyQueue(String msg){
        log.info("接收到惰性队列消息:{}",msg);
    }

(2)进行惰性队列测试

    //测试惰性队列,发送10000条消息
    @Test
    public void testLazyQueue(){
        Message message = MessageBuilder
                .withBody("hello,this is a message belong to lazy.queue".getBytes(StandardCharsets.UTF_8))
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .build();

        for (int i =0;i<10000;i++){
            rabbitTemplate.convertAndSend("lazy.queue",message);
        }
    }
    
    //测试正常队列,发送10000条消息
    @Test
    public void testNormalQueue(){
        Message message = MessageBuilder
                .withBody("hello,this is a message belong to normal.queue".getBytes(StandardCharsets.UTF_8))
                .setDeliveryMode(MessageDeliveryMode.PERSISTENT)
                .build();

        for (int i =0;i<10000;i++){
            rabbitTemplate.convertAndSend("normal.queue",message);
        }
    }

(2)查看MQ页面

惰性队列测试结果:

正常队列测试结果:

消息堆积问题解决方案:

队列上绑定多个消费者,提高消费速度。

给消费者开启线程池,提高消费速度

使用惰性队列,可以在MQ中存储更多消息

惰性队列的优点:

基于磁盘存储,消息上限高

没有间歇性的page-out,性能比较稳定

惰性队列的缺点:

基于磁盘存储,消息时效性会降低

性能受限于磁盘IO

五、RabbitMQ集群

RabbbitMQ是基于Erlang语言编写,而Erlang语言又是一个面向并发的语言,天然支持集群方式,RabbitMQ有两种集群模式:

普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。

镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。

镜像集群虽然支持主从,但主从同步并不是强一致的,某些情况下可能导致数据丢失的风险,因此RabbitMQ在3.8版本以后,退出了新功能:仲裁队列,来代替镜像集群,底层采用Raft协议确保主从数据的一致性。

1.普通集群

普通集群,也叫标准集群(classic cluster)具有以下特征:

(1)会在集群的各节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。

(2)当访问集群节点时,如果队列不在该节点,将会从数据所在节点传递到当前节点把那个返回。

(3)队列所在节点宕机,队列中的消息就会丢失

需要修改配置config,集群中的各节点配置文件相同,然后共享cookie(待补充)

2.镜像集群

镜像集群,本身是主从模式,具有以下特征:

(1)交换机,队列、队列中的消息会在镜像集群的各个节点间同步备份。

(2)创建备份的节点被称为该队列的主节点,备份到的其他节点被称为该队列的镜像节点

(3)一个队列的主节点,可能是另一个队列的镜像节点。

(4)所有操作都是主节点完成,然后同步到镜像节点。

(5)主节点宕机后,镜像节点会成为新的主。

镜像模式的的配置有三种模式:

Exactly模式:

2.仲裁队列

仲裁队列,仲裁队列是3.8版本以后才有的功能,用来代替镜像队列,具备下列特征。

(1)与镜像队列一样都是主从模式,支持主从数据同步。

(2)使用非常简单,没有复杂的配置。

(3)主从同步基于Raft协议,强一致。

1.创建仲裁队列的两种方式

(1)在任意控制台添加一个队列,一定要选择队列类型为Quorum类型。

(2)SpringAMQP创建仲裁队列

@Configuration
public class QuorumConfig {

    @Bean
    public Queue quorumQueue(){
        return QueueBuilder.durable("quorum.queue")//持久化
                .quorum()//仲裁队列
                .build();
    }
}

2.SpringAMQP连接集群,只需要在yaml中配置即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值