目录
一、RocketMQ是什么
二、broker和nameserver
2.1 Broker
2.2 NameServer
三、MQ在秒杀场景下的应用
3.1 利用MQ进行异步操作
3.2 削峰填谷
四、面试题
问1:任何一台 Broker 突然宕机了怎么办?那不就会导致 RocketMQ 里一部分的消息就没了吗?这就会导致 MQ 的不可靠和不可用,这个问题怎么解决?
问2:如果Broker宕了,NameServer是怎么感知到的?
一、RocketMQ是什么
消息中间件的发展:
第一代以ActiveMQ为代表,遵循JMS(java消息服务)规范
第二代以RabbitMQ为代表是一个有Erlang语言开发的AMQP(高级消息队列协议)的开源实现
第三代以kafka为代表,是一代高吞吐、高可用的消息中间件,以及RocketMQ
RocketMQ物理部署结构如下:
几个概念术语:
producer:消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ
producer group: 生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。
consumer : 消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者
consumer group : 消费者组,和生产者类似,消费同一类消息的多个 consumer 实例组成一个消费者组.Consumer实例 的集合。
Topic : Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。
Broker : Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。
Message : Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。
Tag : 标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
Name Server : Name Server 为 producer 和 consumer 提供路由信息。
二、broker和nameserver
2.1 Broker
如下图,我们看看broker是什么,可以看到,每个broker都有一个或多个 Topic(Broker和Topic是多对多的关系),下图的MessageQueue是一个存放消息的队列,一边去存放消息,一边去消费消息。
2.2 NameServer
nameserver提供的路由功能,如下图,例如:
1.Broker 一开始是得向 NameServer注册的,注册的信息包括Broker的ip,Broker负责的消息的Topic,有多少个queue等。
2. 而Producer(生产者)则是会通过NameServer找到Broker,并向Broker中的消息队列(queue)放入消息,如果queue有多个,则会通过轮询的方式来放入消息。
3. 而Consumer(消费者)则是通过NameServer抓取信息,以Topic为单位抓取的,NameServer会返回对应Topic的Broker给Consumer,然后Consumer就可以去Broker上抓取对应的消息了。过程是,Consumer会对Broker上的两个queue分别建立长连接, 如果有消息的话,直接拉走,如果没有消息,Consumer就会长轮询等待,直到有消息放入queue。
4. 当Consumer消费完消息后,Consumer要回复一个信息给对应的queue,告诉queue该消息已经被消费,那么queue就会把该消息删除。
5. 当一个queue被多个Consumer消费的时候,就会出现同步问题,就会有一个锁竞争的问题,因此RocketMQ使用了以queue为单位,平均地分配到各个Consumer,即N个queue就对应N个Consumer,即一个queue就有一个Consumer。所以一个好的中间件,Consumer group中Consumer的数量应该近乎于跟对应的queue的数量相等。Consumer的数量若比queue的数量多,则会有Consumer消费不到消息,若比queue数量少,则会有一个Consumer消费多个消息。
三、MQ在秒杀场景下的应用
3.1 利用MQ进行异步操作
对于一个电商APP而言,每卖掉了一个商品,就要扣减掉商品的库存,而且一旦用户成功支付了,还需要将订单的状态更新成待发货。
在完成这些最核心的功能后,其实是有很多事情要做的,比如上图红色的部分。如果这些动作都以同步方式来完成,根据线上系统的一般统计,多个子步骤全部执行完毕,加起来大概需要1秒~2秒的时间。
有时候在高峰期并发量特别大,服务器的磁盘、IO、CPU的负载会很高,执行SQL语句的性能也会有所下降。因此有的时候甚至需要几秒钟的时间完成上述几个步骤。
那么影响是什么呢?
想象一下,如果你是一个用户,在支付完一个订单之后,界面上会有一个圈圈不停的旋转,让你等待好几秒之后才能提示支付成功。对用户来说几秒钟的时间,会让人非常不耐烦的!
所以首先针对子步骤过多、速度过慢、让用户支付之后等待时间过长的问题,就是订单系统亟需解决的问题!
而解决这个问题的一大利器就是消息中间件,英文全称“Message Queue”,简称MQ。
在引入消息中间件以后,系统A和系统B之间就由同步变为异步通信,而完成这样的一个核心概念就是“消息”。
系统A发送消息给MQ后,就认为已经完成了自己的任务;然后系统B根据自己的情况,可能会在系统A投递消息到MQ之后的1秒内,也可能是1分钟之后,也可能是1小时之后,多长时间都有可能。
反正不管是多长时间后,系统B会根据自己的节奏从MQ里获取到一条属于自己的消息,再根据消息的指示完成自己的工作。
在“异步调用”的整个过程中,系统A仅仅是发个消息到MQ,至于系统B什么时候获取消息,有没有获取消息,系统A是不管的。
举例来说:
在秒杀活动开始前,先把活动商品的库存从数据库中读到redis中,然后用户下单,是从redis中扣减库存的(而不是直接在数据库中扣减库存),然后给MQ发送一个扣减库存的消息,然后消费者在恰当的时候读取这个消息,并从数据库中进行扣减库存的操作。这时候redis的库存往往扣减得速度比数据库快一点,因为数据库是通过MQ异步去扣减的。意思就是说,数据库的库存什么时候去扣减其实是不急的,只要最后能扣减正确就行。
总结:
我们可以让订单系统仅仅完成最核心的功能,然后发送消息到MQ。比如需要进行减库存,就发送一个消息到库存消息队列中,然后库存系统从这个MQ里获取消息再进行处理就可以,把这些很耗时的步骤慢慢执行,从而也实现了系统之间的解耦。
在双11大促活动的时候,同样可以让瞬间涌入的大量下单请求到MQ里去排队,然后让订单系统在后台慢慢的获取订单,以数据库可以接受的速率完成操作,避免瞬间请求量过大击垮数据库。
3.2 削峰填谷
MQ 除了可以使用异步的方式实现系统间的解耦,更可以在双 11 这样的秒杀活动中,通过削峰填谷的方式,处理瞬时间涌入的大量请求。
什么是削峰填谷?
削峰填谷本身是电力行业的概念,电力企业通过必要的技术和管理手段,降低电网的高峰负荷,提高低谷负荷,平滑负荷曲线,提高负荷率,保证电网的稳定运行。
假设一个应用,它能够每秒处理 1000 个请求。如果在第一秒接收到 2000 个请求,而接下来的两秒都没有请求到达。
(1)在第一秒被 2000 个请求直接压垮;
(2)假设第一秒没有被压垮,它在这一秒时间内只能处理 1000 请求,第二第三秒却完全空闲,浪费了系统资源。
红色的部分是超出系统处理能力的部分,可以把红色的那部分消息平摊到后面空闲时去处理,这样既可以保证系统负载处在一个稳定的水位,又可以尽可能地处理更多消息。
针对秒杀的场景,上游发起高并发的下单操作,由于下游处理能力有限,两端速度不匹配。此时我们引入 MQ 可以对流量进行缓冲,并实现削峰填谷。
上游速度很快,每秒发起五万个请求也没关系,它只管往 MQ 中发。下游系统虽然每秒只能处理 1000 个请求,但它完全可以 follow 自己的节奏,每隔一段时间,主动拉取若干条信息,实施限流的效果,保护自身。
3.3 秒杀大闸
为了解决秒杀令牌在活动一开始无限制生成(因为主要用户不停地访问,就会不停地创建令牌),影响系统的性能,提出了秒杀大闸的解决方案;
活动开始前,把预设的令牌数量放到redis中,然后活动开始后,用户调用秒杀接口生成令牌时,生成一个令牌,把redis中的令牌数量减一,直到减为0则不再发放令牌。
主要是为了限制令牌的个数,使其不会无限被生成。
四、面试题
问1:任何一台 Broker 突然宕机了怎么办?那不就会导致 RocketMQ 里一部分的消息就没了吗?这就会导致 MQ 的不可靠和不可用,这个问题怎么解决?
答:
RocketMQ的解决思路是Broker主从架构以及多副本策略。
Master收到消息后会同步给Slave,这样一条消息就不止一份了,Master宕机了还有slave中的消息可用,保证了MQ的可靠性和高可用新。
问2:如果Broker宕了,NameServer是怎么感知到的?
答:
Broker会定时(30s)向NameServer发送心跳
然后 NameServer会定时(10s)运行一个任务,去检查一下各个Broker的最近一次心跳时间,如果某个Broker超过120s都没发送心跳了,那么就认为这个Broker已经挂掉了。