面试题一 消息中间件及缓存

一、系统里为什么要用消息队列

答题思路:

1、你们公司项目里有个什么场景?

2、这个场景有什么技术挑战,如果不用MQ会很麻烦,用了MQ会带来很多好处

参考:

1、比较核心的使用场景有3个

1)解耦:A要发送数据给B,过几天又要发送数据给C,过几天又要发送数据给D,这会导致A频繁改动代码。并且现在往B、C、D发送,如果B挂了,其他没挂,那我发的数据需要缓存重试吗?

2)异步:A调用B、B调用C、C调用D,如果全用接口,只有有一个地方调用慢,整个请求都会变慢,客户使用就会觉的很卡

3)削峰:0到12点每秒并发只有100个,12点后每秒并发达到10000个,并发暴增导致接口调用延迟,客户使用就会觉的很卡

当然还有一些其他场景,比如实现分布式事务最终一致性,但主要是上面3个场景

二、消息队列有什么缺点

1、缺点

1)系统变复杂,可用性降低。比如MQ挂了导致系统崩溃

2)系统要考虑的东西变多,进而导致系统复杂性变高。比如怎么防止消息丢失?怎么防止消息重复消费?怎么防止消息被顺序消费? 

3)会有一致性问题。比如A给B、C、D都发消息,B和C成功了,D失败了,此时该怎么办?

三、四种MQ的区别,各自适应什么场景

四、如何保证消息中间件的高可用

1、RabbitMQ的高可用(RabbitMQ不是分布式的)

1)单机模式

2)普通集群模式

由于RabbitMQ不是分布式的,只能集群部署,所以实际上数据只能存在一台机器上。比如有3个RabbitMQ节点,数据存在节点A上,当消费者连接节点B、C消费时,这2个节点会从A拉取消息给消费者。

缺点:

  • 会在RabbitMQ集群内部产生大量的数据传输
  • 几乎没有可用性保证,如果节点A宕机,那么整个集群就挂了

3)镜像集群模式

与普通集群模式不同,镜像集群模式会把数据同步到每一个节点。可以在RabbitMQ的管理控制台中设置策略是镜像集群模式

缺点:

  • 没有扩展性,当数据量很大时,加机器也没用,因为它会同步所有数据,占据了很大的硬盘空间

2、Kafka的高可用(Kafka是分布式的)

一个Topic分为多个partition,每个partiion存在不同的broker上,每个partition就放一部分数据。主从备份、leader选举,防止节点宕机导致数据缺失

3、RocketMQ的高可用

一个Topic分为多个MessageQueue,这些MessageQueue分布在不同的Broker上,每个MessageQueue对应一份ConsumeQueue文件(路径名:store/consumequeue/{TopicName}/{QueueId})。一个Broker上可以有多个Topic,但数据都存储在同一个CommitLog文件中。ConsumeQueue文件不存储具体的数据,只存储数据的tag和在CommitLog文件中的offset

五、如何保证不重复消费数据

消费者端通过业务来保证不重复消费数据。比如每次消费时,先查数据库看有没有这条记录了,没有再insert,有的话就不消费

六、如何保证消息不丢失

数据丢失可能的原因:

  • 生产者发送数据时,数据丢失
  • 生产者发送消息到了中间件,中间件暂存在内存中,但此时却宕机了,导致内存中的消息没了
  • 消费者消费了消息,但还没处理就挂掉了,中间件以为消费者消费完了

解决方案:

  • 使用消息中间件提供的事务消息功能,保证生产者发送消息不丢失
  • 让中间件收到消息后回调生产者,告诉生产者接收成功还是失败,保证生产者发送消息不丢失
  • RabbitMQ开启持久化,保证写入到中间件的数据不会丢失
  • RocketMQ开启同步刷盘,保证写入到中间件的数据不会丢失
  • Kafka设置4个参数:在topic设置replication.factor > 1,保证至少2个副本;在kafka服务端设置min.insync.replicas > 1,要求一个leader至少有一个follower还存活;在生产者端设置acks = all,要求每条数据必须写入所有副本才算成功;在生产者端设置retries = MAX,如果写入失败,就不断重试,卡在这里了
  • 消费者端消费成功后,才返回ack给中间件,保证消费者不会丢失数据

七、如何保证消息是顺序的

生产者只写入一个Queue/Partition,消费端只让一台机器来消费,消费时使用单线程或内存队列来消费

八、发生生产事故,导致挤压了几百万的消息未消费该怎么办

  • 如果消息不重要,可以丢失,那么修改消费者代码,将积压的消息都不做任何处理直接消费掉。然后再改回来,正常处理实时消息
  • 如果MessageQueue数量明显大于消费者机器数,那么就临时增加机器给消费者,提高消费速度,尽快将积压的消息处理完,但要注意数据库等能否抗住
  • 如果MessageQueue数量跟消费者机器数差不多,那么增加机器给消费者也没有用。可以修改消费者代码,将积压的消息写入新的Topic,Topic的MessageQueue多弄些,再增加机器给消费者消费这个新的Topic。最后再改回来

九、如果让你自己设计一个消息中间件,你会怎么做

支持扩容,数据要落入磁盘,落入磁盘要顺序写(效率高),Raft协议实现高可用,数据零丢失

十、缓存+数据库不一致解决方案

初步方案:先删除缓存,再更新数据库。但这样在高并发下会有问题,比如线程A删除了缓存,正准备更新数据库,此时线程B需要查询数据,发现缓存里没有,就去数据库里查询并放入缓存。这样依旧会导致缓存与数据库不一致。 

进阶方案:将请求放入队列中,串行执行。比如一个更新请求,一个查询请求,都进入了队列中。更新请求先删缓存,再更新数据库,查询请求查数据库,再更新缓存。这样就能做到缓存与数据库一致。但效率不高,所以为了提高效率,不能所有的查询请求都入队列。最好是如果有更新请求进来了,就入队列,然后只放一个查询请求进队列,其他的查询请求等待,等到队列中的更新和查询请求执行完了,再把其他的查询请求放行。如果更新的并发操作比较多,都放在一个串行队列中,那么最后一个更新操作就会花费较多的时间等待前面的完成,所以可以串行队列也弄几个,更新同一个表的进同一个队列,提高一下效率。

你可能感兴趣的:(面试题,面试)