乐趣区

关于rabbitmq:『假如我是面试官』RabbitMQ我会这样问

1. 为什么你们公司抉择 RabbitMQ 作为消息中间件

在音讯队列选型时,咱们调研了市场上比拟罕用 ActiveMQ,RabbitMQ,RocketMQ,Kafka。

  1. RabbitMQ 绝对成熟稳固,这是咱们抉择它最次要的起因。
  2. 社区比拟沉闷,有欠缺的材料能够参考。
  3. Rabbitmq 的吞吐量能够达到万级,齐全满足咱们零碎的要求。
  4. RabbitMQ 是 Erlang 语言开发的,性能比拟好。
  5. 有欠缺的可视化界面,不便查看。

2. 音讯队列的长处和毛病有哪些

长处有:

  • 异步解决 – 相比于传统的串行、并行形式,进步了零碎吞吐量。
  • 利用解耦 – 零碎间通过音讯通信,不必关怀其余零碎的解决。
  • 流量削锋 – 能够通过音讯队列长度管制申请量;能够缓解短时间内的高并发申请。

毛病有:

  • 零碎可用性升高
  • 零碎复杂度进步

3. RabbitMQ 罕用的工作模式有哪些

2.1 简略模型

  • p:生成者
  • C:消费者
  • 红色局部:quene,音讯队列

2.2 工作模型

这种模式下一条音讯只能由一个消费者进行生产,默认状况下,每个消费者是轮询生产的。

  • p:生成者
  • C1、C2:消费者
  • 红色局部:quene,音讯队列

2.3 公布订阅模型(fanout)

这种模型中生产者发送的音讯所有消费者都能够生产。

  • p:生成者
  • X:交换机
  • C1、C2:消费者
  • 红色局部:quene,音讯队列

2.4 路由模型(routing)

这种模型消费者发送的音讯,不同类型的音讯能够由不同的消费者去生产。

  • p:生成者
  • X:交换机,接管到生产者的音讯后将音讯投递给与 routing key 齐全匹配的队列
  • C1、C2:消费者
  • 红色局部:quene,音讯队列

2.5 主题模型(topic)

这种模型和 direct 模型一样,都是能够依据 routing key 将音讯路由到不同的队列,只不过这种模型能够让队列绑定 routing key 的时候应用通配符。这种类型的 routing key 都是由一个或多个单词组成,多个单词之间用 . 宰割。

通配符介绍:

*:只匹配一个单词

#:匹配一个或多个单词

4. 如何保障音讯不失落(如何保障音讯的可靠性)

一条音讯从生产到生产经验了三个阶段,别离是生产者,MQ 和消费者,对于 RabbitMQ 来说,音讯的传递还波及到交换机。因而 RabbitMQ 呈现音讯失落的状况有四个

别离是

  1. 音讯生产者没有胜利将音讯发送到 MQ 导致音讯失落
  2. 交换机未路由到音讯队列导致音讯失落
  3. 音讯在 MQ 中时,MQ 产生宕机导致音讯失落
  4. 消费者生产音讯时出现异常导致音讯失落

针对下面提到的四种状况,别离进行解决

  1. amqp 协定提供了事务机制,在投递音讯时开启事务,如果音讯投递失败,则回滚事务,很少有人去应用事务。除了事务之外,RabbitMQ 还提供了生产者确认机制(publisher confirm)。生产者将信道设置成 confirm(确认)模式,一旦信道进入 confirm 模式,所有在该信道下面公布的音讯都会被指派一个惟一的 ID(从 1 开始),一旦音讯被投递到所有匹配的队列之后,RabbitMQ 就会发送一个确认(Basic.Ack)给生产者(蕴含音讯的惟一 ID),这就使得生产者通晓音讯曾经正确达到了目的地了。
# 开启生产者确认机制,# 留神这里确认的是是否达到交换机
spring.rabbitmq.publisher-confirm-type=correlated
@RestController
public class Producer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("send")
    public void sendMessage(){
        /**
         * 生产者确认音讯
         */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {System.out.println(correlationData);
                System.out.println(ack);
                System.out.println(cause);
            }
        });
        rabbitTemplate.convertAndSend("s","error","这是一条谬误日志!!!");
    }
}
  1. 音讯从交换机未能匹配到队列时将此条音讯返回给生产者
spring.rabbitmq.publisher-returns=true
@RestController
public class Producer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("send")
    public void sendMessage(){
        /**
         * 音讯未达队列时返回该条音讯
         */
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {System.out.println(returnedMessage);
            }
        });
        rabbitTemplate.convertAndSend("s","error","这是一条谬误日志!!!");
    }
}
  1. 音讯在交换机或队列中产生失落,咱们只须要将交换机和队列进行长久化。
/**
  * 定义一个长久化的 topic 交换机
  * durable 长久化
  * @return
 */
@Bean
public Exchange exchangeJavatrip(){return ExchangeBuilder.topicExchange(EXCHANGE).durable(true).build();}

/**
 * 定义一个长久化的队列
 * durable 长久化
 * @return
 */
@Bean
public Queue queueJavatrip(){return QueueBuilder.durable(QUEUE).build();}
  1. 消费者开启手动签收模式,生产实现后进行 ack 确认。
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitListener(queues = MqConfig.QUEUE)
public void receive(String body, Message message, Channel channel) throws Exception{long deliveryTag = message.getMessageProperties().getDeliveryTag();
    System.out.println(deliveryTag);
    // 零碎业务逻辑判断是否签收
    if(deliveryTag % 2 == 0){channel.basicAck(deliveryTag,false);
    }else{
        // 第二个参数是否批量确认,第三个参数是否从新回队列
        channel.basicNack(deliveryTag,false,true);
    }
}

5. 如何保障音讯不反复生产(如何保障音讯的幂等性)

音讯反复的起因有两个:

  1. 生产时音讯反复

    因为生产者发送音讯给 MQ,在 MQ 确认的时候呈现了网络稳定,生产者没有收到确认,实际上 MQ 曾经接管到了音讯。这时候生产者就会从新发送一遍这条音讯。

  2. 生产时音讯反复。

    消费者生产胜利后,在给 MQ 确认的时候呈现了网络稳定,MQ 没有接管到确认,为了保障音讯被生产,MQ 就会持续给消费者投递之前的音讯。这时候消费者就接管到了两条一样的音讯。

因为音讯反复是网络稳定等起因造成的,无奈防止,咱们能做的的就是保障音讯的幂等性,以防业务反复解决。具体解决计划为:

让每个音讯携带一个全局的惟一 ID,即可保障音讯的幂等性,具体生产过程为:

  1. 消费者获取到音讯后先依据 id 去查问 redis/db 是否存在该音讯。
  2. 如果不存在,则失常生产,生产结束后写入 redis/db。
  3. 如果存在,则证实音讯被生产过,间接抛弃。
@RabbitListener(queues = MqConfig.QUEUE)
public void receive(Message message, Channel channel){String messageId = message.getMessageProperties().getMessageId();
    String body = new String(message.getBody());
    String redisId = redisTemplate.opsForValue().get(messageId)+"";
    // 如果 redis 中存有以后音讯的音讯 id
    // 则证实生产过
    if(messageId.equals(redisId)){return;}
    redisTemplate.opsForValue().set(messageId, UUID.randomUUID());
}

6. 音讯大量沉积应该怎么解决

音讯沉积的起因有两个

  1. 网络故障,消费者无奈失常生产
  2. 生产方生产后未进行 ack 确认

解决方案如下:

  1. 查看并修复消费者故障,使其失常生产
  2. 编写长期程序将沉积的音讯发送到容量更大的 MQ 集群,减少消费者疾速生产
  3. 沉积音讯生产结束后,进行长期程序,恢复正常生产

7. 死信是什么?死信如何解决

当一条音讯在队列中呈现以下三种状况的时候,该音讯就会变成一条死信。

  • 音讯被回绝(basic.reject / basic.nack),并且 requeue = false
  • 音讯 TTL 过期
  • 队列达到最大长度

当音讯在一个队列中变成一个死信之后,如果配置了死信队列,它将被从新 publish 到死信交换机,死信交换机将死信投递到一个队列上,这个队列就是死信队列。

一条音讯成为死信后,个别会通过死信队列进行存库,而后定时将库中的死信进行从新投递到音讯队列上。

8. 如果我有一笔订单,30 分钟未领取则敞开订单,应用 RabbitMQ 如何来实现

RabbitMQ 能够应用死信队列来实现延时生产,用户下单之后,将订单信息投递到音讯队列中,并且设置音讯过期时常为 30 分钟。如果用户领取则失常敞开订单,如果用户未领取,音讯达到过期工夫,音讯会进入死信替换,由消费者进行生产死信队列来敞开订单。

9. RabbitMQ 如何保障高可用

RabbitMQ 有两种集群模式,别离是一般集群和镜像集群,一般模式无奈保障 RabbitMQ 的高可用。

一般集群

如果有三个节点,rabbitmq1、rabbitmq2、rabbitmq3,音讯实际上只存在于其中一个节点,三个节点仅有雷同的元数据,即队列的构造,当音讯进入 rabbitmq2 节点的 queue 后,consumer 从 rabbitmq1 的节点进行生产,rabbitmq1 和 rabbitmq2 会进行长期通信,从 rabbitmq2 中获取音讯而后返回给 consumer。

这种模式存在以下两个问题:

  1. 当 rabbitmq2 宕机后,音讯无奈失常生产,没有做到真正的高可用
  2. 理论数据还是在单个实例上,存在瓶颈问题

镜像集群

如果有三个节点,rabbitmq1、rabbitmq2、rabbitmq3,每个实例之间都能够互相通信,每次生产者写音讯到 queue 的时候,每个 rabbitmq 节点上都有 queue 的音讯数据和元数据。这种模式应用于可靠性要求较高的场景。

点关注、不迷路

如果感觉文章不错,欢送关注、点赞、珍藏,你们的反对是我创作的能源,感激大家。

如果文章写的有问题,请不要吝惜文笔,欢送留言指出,我会及时核查批改。

如果你还想看到更多别的货色,能够微信搜寻「Java 旅途」进行关注。回复“手册”支付 Java 面试手册!

退出移动版