消息队列

715 阅读5分钟

为什么使用消息队列,或者说消息队列有哪些作用?

消息队列的作用:解耦、异步、削峰

解耦

A系统的数据被B,C,D系统依赖,A系统需要要把数据依次发给B,C,D,如果A直接调用B,C,D的接口来发送数据,那么可能存在的问题:

  • 如果B系统出现问题,导致A系统调用B系统的接口阻塞,那C,D系统也都会受到影响;A系统还需要考虑其他三个系统是否正常运行,是否需要重发数据。
  • 如果某一时刻D系统不再需要A系统的数据了,那么需要将A调用D接口的代码删除掉;同样,如果新加入了一个E系统也需要A的数据,那么A需要添加一个E的接口调用代码。

如果使用消息队列,A只需要将数据发送到些消息消息队列,其他需要该数据的系统只需要主动去拉取就行了。这样就把A系统与其他系统解耦了。

异步

A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。 如果A系统将需要入库的数据写入三个消息队列,BCD只需要到自己的消息队列里拿消息消费,假设A写消息的的耗时是1ms,那么一个请求只需要3 + 1 + 1 + 1 = 5ms,对用户来说是无感知的。

削峰

A系统在00:00到12:00这个时间段的请求量很小,大概每秒100个请求,而在12:00到13:00这个时间段,每秒请求数会达到5000,之后还是每秒大概100个请求,而A系统能承受的最高QPS为2000,如果在12:00到13:00这段时间直接将请求发送给A系统去执行,那么在12:00到13:00这段时间里,会导致A系统崩溃掉。如果我们先将请求发送到消息队列里,然后A系统以每秒2000的速度去拉取请求并执行,那么在高峰期,系统依然能够正常运行,只不过会导致一定的请求积压以及造成的延时,在高峰期之后,A系统依然会尽最大努力消费请求,所以最终请求都能够得到处理。

引入消息队列会带来哪些风险,或者说消息队列的缺点有哪些?

系统可用性降低,系统复杂度提高,会出现一致性问题

系统可用性降低

原本A系统只需要掉BCD接口即可,BCD自己保证可用就行,现在引入消息队列作为桥梁,如果消息队列挂掉,系统就不可用了。怎样保证消息队列高可用是个问题。

系统复杂度提高

加入消息队列,会引入一系列的问题:怎样保证消息不被重复消费?如果消息丢失怎么办?怎样保证消息的消费顺序?

一致性问题

当A系统把消息发送到消息队列,BCD去消费,BCD是否成功消费了消息是不可预知的,有可能B消费成功了,CD都没有成功,这就造成了数据不一致的情况。

如何保证消息队列的高可用行?

RabbitMQ

RabbitMQ有三种部署模式:单机模式(demo),普通集群模式(无高可用),镜像集群模式(高可用)

普通集群模式

多台机器,每台机器一个mq实例,每个queue只能存在于一个mq实例中,但是每个实例都有集群中所有queue的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。该模式并不能保证高可用,假如一个节点挂掉,那么该节点上的所有queue里的消息都无法被消费,如果开启消息持久化,消息不一定会丢失,但是只能等实例恢复才能被消费。消费的方式有两种,一是随机连接实例,这样会有集群间消息拉取的开销,要么只连接到queue所在实例,这样会有单实例性能瓶颈。

镜像集群模式

也就是每个实例都保存了完整的queue的信息和数据,这样即使有实例宕机也不影响消息的消费,这样就做到了高可用。但是这并不是分布式,所以不能进行queue的水平扩展,集群能够容纳多少queue和多少消息受单台机器的资源制约,没有扩展性。

Kafka

Kafka基本架构:由多个broker组成,每个broker是一个节点;可以创建一个topic,每个topic可以分为多个partition,每个partition存在于不同的broker,每个partition存放一部分数据。kafka是天然的分布式消息队列,也就是说一个topic存放在多个机器上,一台机器存放一部分数据。Kafka 0.8以后提供replica(副本)机制,每个partition可以有多个副本,每个副本存放在不同的机器上,这样即使一个副本所在的机器有问题,依然能获取到topic的全部数据。所有的replica会选举出一个leader,写的时候leader会把数据同步给follower,读的时候直接读取leader就可以了。Kafka会均匀地把replica分布在不同的机器上。