消息队列简单了解
这消息队列虽然用起来好像很简单,但概念乱七八糟的,还是记一下吧。
作用
消息队列( MessageQueue,下称 MQ )的三大作用:解耦、异步、削峰;
解耦
假设现在的一个应用包含多个微服务,其中的 A 服务处理完一个请求后,需要将数据发送给 B、C、D 服务,原本的做法是在代码中硬编码发送数据,这样造成了 A 与其他系统的强耦合。哪天 B 服务不需要这个数据了,除了修改 B 服务的代码,还要改 A 服务;哪天新增了一个 E 服务需要这个数据,还要在 A 服务中添加发送数据到 E 服务的代码。如果服务越来越多,或者数据越来越复杂,这样的强耦合是很难接受的。
此时就可以使用 MQ 达到解耦的目的:A 服务将数据作为消息发送给 MQ ,当某个服务需要这个数据时,直接从 MQ 中获取就可以了。通过增加 MQ 这个中间件,解除了 A 服务与其他服务直接的耦合关系。
异步
还是这个应用,假设 A 服务收到用户的请求后,需要调用 B、C、D 服务处理这个请求,都处理完才能返回,这样就导致 这个请求的处理时间 = A + B + C + D
。并且 A 作为一个转发者,自身的处理时间是很短的,可能会出现 50ms + 300ms + 300ms + 400ms = 1050ms
,一个请求要走1秒,作为一个 WEB 应用,这样的延迟也是挺难接受的。
此时使用 MQ ,可以实现异步处理请求,达到降低延迟的目的:A 服务接受到请求,将消息发送到 MQ ,只要发送到 MQ 成功,就可以应答这个请求成功了。而具体处理这个请求的 B、C、D 服务,则可以同时去 MQ 中取到这个消息,处理请求内容,此时它们是异步的,只需要 400ms 就可以处理完(取最大的)。而对于用户而言,这个请求好像只处理了 50ms,太快勒。
削峰
换一个应用,这个应用中的 A 服务接受用户请求,这个请求需要查询数据库,平时没什么问题。但某次搞活动,晚上八点开始抢东西,一堆用户搁那疯狂刷新,不仅人多,请求次数也多。A 服务收到请求就直接到数据库中查询,压力给到了数据库。以 MySQL 来说,每秒处理 2000 个查询已经压力很大了,再大数据库就要顶不住崩溃了,但用户的请求数量可远大于 2000 个。
此时可以使用 MQ 达到削峰的目的:用户的请求发送到 MQ 保存起来(是有顺序的,保证了用户抢东西的请求先后顺序仍一致),A 服务为了照顾数据库的并发能力,每秒只取 2000 个请求到数据库中进行查询,这样虽然消息队列会积压很多请求,但保证了数据库不会崩溃,并且后续也会把积压的请求处理完。
缺点
万物皆有其优缺点,一个应用使用了消息队列享受了它的好处,也要承受它的坏处:
- 消息队列降低了系统的耦合度,但也降低了系统可用性。在解耦的例子中,一旦作为中间件的消息队列出了问题,那 A 服务和其他服务直接的联系直接断掉了,系统几乎是崩溃了。
- 消息队列提供了系统的复杂程度。引入了消息这个概念,就要处理诸如消息重复消费、消息丢失、消息顺序等等问题。
- 消息队列存在一致性隐患。在异步的例子中,A 服务将消息存入队列,应答返回成功。但在 B、C、D 服务中,可能 B、C 服务执行成功,D 服务出现问题失败了,此时应该数据回滚,但又已经告知用户请求成功,这可咋整。
模型
队列模型
消息队列最初就真的只是一个队列:维护一个存放消息的先进先出(FIFO)队列,生产者生产消息,将消息入队;消费者消费消息,将消息出队。
但这种简单的实现自然是存在问题:多个生产者可以将消息存到同一消息队列中,由于先进先出策略,消费消息的顺序和消息入队的顺序一致;但如果存在多个消费者消费同一消息队列,则它们属于竞争关系,一条消息被一个消费者消费出队后,其他消费者就消费不到了。
在目前的消息队列产品中,RabbitMQ 是少数来在坚持使用队列模型的产品之一,它提供 Exchange 模块,位于生产者和消费者之间。生产者无需关注将消息发给哪个队列,而是直接将消息发给 Exchange,由 Exchange 上配置的策略再决定将消息队列发送到哪个队列中。
发布-订阅模型
发布-订阅模型的出现就是为了解决队列模型中遇到的多个消费者都消费所有信息的问题。在发布-订阅模型中,发送消息的叫做发布者,接收消息的叫做订阅者,存放消息的容器叫做主题。发布者将消息发送到主题中,订阅者需要先订阅主题才能收到消息。
对应队列模型,发布者就是生产者,订阅者就是消费者,主题就是队列,没有本质区别,最大的区别就是主题里的消息是可以被消费多次的。当发布-订阅模型中的订阅者只有一个时,它就和队列模型差不多了。因此,发布-订阅模型是兼容队列模型的。
RocketMQ 使用的消息模型就是标准的发布-订阅模型,并再次加入了队列(和队列模型的队列,都是数据结构中的队列,但作用不一样...)的概念;Kafka 采用的也是发布-订阅模型,与 RocketMQ 相比,只是队列的概念换成了分区。
RocketMQ
简单记一下 RocketMQ 里的概念。
Message
Message(消息)就是要传输的信息。一条消息必须有一个主题(Topic),主题就是这个消息的分类(比如这篇东西有一个随笔分类),一般,相同业务的消息的主题就是一样的。
消息还有一个可选的标签(Tag),标签就是这个消息的记号,可以用于查找消息(比如我这篇东西也有一个标签)。一般,一个主题下的消息根据目的不同就有不同的标签,如请求消息、应答消息。
Topic
Topic(主题)可以理解为消息的分类,它是消息的第一级分类,一条消息必须有一个 Topic 。
主题与生产者和消费者的关系没有限制,对于一个主题,可能有 0-N 个生产者向其发送消息,一个生产者也可以同时向不同的主题发送消息;一个主题也可以被 0-N 个消费者订阅。
Tag
Tag (标签)如其名,就是一个标签,它是消息的第二级分类,使用户可以灵活的管理消息,一条消息可以没有标签。
使用标签,同一业务的消息都在一个主题中,但可以通过标签区分这些消息:如订单消息可以分为订单创建、订单支付等。
Group
在 RocketMQ 中,消费者组(Consumer Group)就是具体化的订阅者。每个消费者组都会消费它订阅的主题中的每条消息,即多个消费者组订阅一个主题,主题中的每条消息会被多个消费者组都消费一次。但在消费者组内部,每个消费者是竞争关系,一条消息只会被组内的一个消费者消费。
Message Queue
这就是之前所说的 RoketMQ 加入的队列(Queue)概念,一个主题下,可以设置多个队列。如果订阅者要获取主题下的所有消息,就要遍历其中的所有队列。
目前看来,应该是每个队列对应了一个消费者组?
几乎所有的消息队列都使用一种“ 请求-确认 ”机制,确保消息不会在传递过程中由于网络或者服务器故障而丢失,具体做法:
- 在生产端,生产者先将消息发送给服务器,服务器在收到消息并将其放入到主题或者队列后,给生产者发送确认的响应。
- 在消费端,消费者在收到消息并完成自己的业务逻辑后,给服务端发送消息消费成功的确认。
这个机制保证了消息在传递过程中的可靠性,但是引入这个机制后,为了确保消息的有序消费,在某一条消息被成功消费之前,下一条消息是不能被消费的。即每个主题在任意时刻,至多只能有一个消费者在消费,这也就没有办法水平扩展消费者的数量来提升消费端整体性能。
为了解决这个问题,RocketMQ引入了队列的概念,在每个主题中包含多个队列,通过多个队列来实现多实例并行消费消息。RocketMQ只保证消息在队列上是有序消费的,在主题层面是无法保证的。
Offset
在主题中,一条消息可能被多个消费者组消费,因此被消费后消息不会马上删除。因此需要 RocketMQ 为每个消费者组在每个队列上维护一个消费位置(Offset),标记当前消费消息的进度。
这个标记表明之前的消息已经被消费,而之后的都没有被消费。当消费了一条新消息后,位置就会加一。Offset 就是 Queue 的下标。
标签:消费,服务,请求,队列,主题,了解,消息,简单 From: https://www.cnblogs.com/qiyuanc/p/Back8.html